4087 lines
108 KiB
C++
4087 lines
108 KiB
C++
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1999
|
||
|
//
|
||
|
// File: dsUtil.cpp
|
||
|
//
|
||
|
// Contents: Utility functions
|
||
|
//
|
||
|
// History: 08-Nov-99 JeffJon Created
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
#include "dsutil.h"
|
||
|
|
||
|
#include "dssnap.h"
|
||
|
#include "gsz.h"
|
||
|
#include "helpids.h"
|
||
|
#include "querysup.h"
|
||
|
|
||
|
#include "wininet.h"
|
||
|
#include <dnsapi.h>
|
||
|
#include <objsel.h>
|
||
|
#include <ntldap.h> // LDAP_MATCHING_RULE_BIT_AND_W
|
||
|
#include <lmaccess.h> // UF_SERVER_TRUST_ACCOUNT
|
||
|
#include <ntdsapi.h> // DsRemoveDsServer
|
||
|
#include <ntsecapi.h> // Lsa*
|
||
|
|
||
|
UINT g_cfDsObjectPicker = RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
|
||
|
|
||
|
//
|
||
|
// Common DS strings
|
||
|
//
|
||
|
PCWSTR g_pszAllowedAttributesEffective = L"allowedAttributesEffective";
|
||
|
PCWSTR g_pszPwdLastSet = L"pwdLastSet";
|
||
|
|
||
|
|
||
|
//
|
||
|
// This is a wrapper for ADsOpenObject. It gives DSAdmin a single point to change
|
||
|
// global flags that are passed to ADsOpenObject without have to search and replace
|
||
|
// all occurrences in the code
|
||
|
//
|
||
|
HRESULT DSAdminOpenObject(PCWSTR pszPath,
|
||
|
REFIID refIID,
|
||
|
PVOID* ppObject,
|
||
|
BOOL bServer)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
DWORD dwFlags = ADS_SECURE_AUTHENTICATION;
|
||
|
|
||
|
if (bServer)
|
||
|
{
|
||
|
//
|
||
|
// If we know we are connecting to a specific server and not domain in general
|
||
|
// then pass the ADS_SERVER_BIND flag to save ADSI the trouble of figuring it out
|
||
|
//
|
||
|
dwFlags |= ADS_SERVER_BIND;
|
||
|
}
|
||
|
|
||
|
hr = ADsOpenObject((LPWSTR)pszPath, NULL, NULL, dwFlags, refIID, ppObject);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: GetServerFromLDAPPath
|
||
|
//
|
||
|
// Synopsis: Gets the server portion of an LDAP Path
|
||
|
//
|
||
|
// In:
|
||
|
// LPCWSTR - pointer to string to convert
|
||
|
//
|
||
|
// Out:
|
||
|
// BSTR* - pointer to a BSTR containing the server name
|
||
|
//
|
||
|
// Return:
|
||
|
// HRESULT - whether the operation completed successfully
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
HRESULT GetServerFromLDAPPath(IN LPCWSTR lpszLdapPath, OUT BSTR* pbstrServerName)
|
||
|
{
|
||
|
if (pbstrServerName == NULL)
|
||
|
{
|
||
|
return E_POINTER;
|
||
|
}
|
||
|
else if (*pbstrServerName != NULL)
|
||
|
{
|
||
|
::SysFreeString(*pbstrServerName);
|
||
|
*pbstrServerName = NULL;
|
||
|
}
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
HRESULT hr = pathCracker.Set((LPWSTR)lpszLdapPath, ADS_SETTYPE_FULL);
|
||
|
RETURN_IF_FAILED(hr);
|
||
|
|
||
|
return pathCracker.Retrieve(ADS_FORMAT_SERVER, pbstrServerName);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL StripADsIPath(LPCWSTR lpszPath, CString& strref, bool bUseEscapedMode /* = true */)
|
||
|
{
|
||
|
if (lpszPath == NULL)
|
||
|
{
|
||
|
strref = L"";
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (wcslen(lpszPath) == 0)
|
||
|
{
|
||
|
strref = lpszPath;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
|
||
|
if ( bUseEscapedMode )
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
|
||
|
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
|
||
|
HRESULT hr = pathCracker.Set((PWSTR)lpszPath, ADS_SETTYPE_FULL);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TRACE(_T("StripADsIPath, IADsPathname::Set returned error 0x%x\n"), hr);
|
||
|
strref = lpszPath;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CComBSTR bsX500DN;
|
||
|
|
||
|
(void) pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bsX500DN);
|
||
|
strref = bsX500DN;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// support routines for Add To Group function
|
||
|
|
||
|
void RemovePortifPresent(CString *csGroup)
|
||
|
{
|
||
|
CString x, y;
|
||
|
int n = csGroup->Find (L":3268");
|
||
|
if (n > 0) {
|
||
|
x = csGroup->Left(n);
|
||
|
y = csGroup->Right(csGroup->GetLength() - (n+5));
|
||
|
*csGroup = x+y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#if (FALSE)
|
||
|
HRESULT AddDataObjListToGroup(IN CObjectNamesFormatCracker* pNames,
|
||
|
IN HWND hwnd)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
TRACE (_T("CDSContextMenu::AddToGroup\n"));
|
||
|
|
||
|
STGMEDIUM stgmedium =
|
||
|
{
|
||
|
TYMED_HGLOBAL,
|
||
|
NULL,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
FORMATETC formatetc =
|
||
|
{
|
||
|
(CLIPFORMAT)g_cfDsObjectPicker,
|
||
|
NULL,
|
||
|
DVASPECT_CONTENT,
|
||
|
-1,
|
||
|
TYMED_HGLOBAL
|
||
|
};
|
||
|
|
||
|
// algorithm
|
||
|
// examine selection, figure out classes
|
||
|
// figure out what groups are possible
|
||
|
// call object picker, get a group
|
||
|
// for each object in selection
|
||
|
// if container
|
||
|
// procees_container()
|
||
|
// else
|
||
|
// process_leaf()
|
||
|
//
|
||
|
|
||
|
|
||
|
//
|
||
|
// Create an instance of the object picker.
|
||
|
//
|
||
|
|
||
|
IDsObjectPicker * pDsObjectPicker = NULL;
|
||
|
|
||
|
hr = CoCreateInstance(CLSID_DsObjectPicker,
|
||
|
NULL,
|
||
|
CLSCTX_INPROC_SERVER,
|
||
|
IID_IDsObjectPicker,
|
||
|
(void **) &pDsObjectPicker);
|
||
|
if (FAILED(hr))
|
||
|
return(hr);
|
||
|
//
|
||
|
// Prepare to initialize the object picker.
|
||
|
//
|
||
|
// first, get the name of DC that we are talking to.
|
||
|
CComBSTR bstrDC;
|
||
|
LPCWSTR lpszPath = pNames->GetName(0);
|
||
|
GetServerFromLDAPPath(lpszPath, &bstrDC);
|
||
|
|
||
|
//
|
||
|
// Set up the array of scope initializer structures.
|
||
|
//
|
||
|
|
||
|
static const int SCOPE_INIT_COUNT = 1;
|
||
|
DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
|
||
|
int scopeindex = 0;
|
||
|
ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// The domain to which the target computer is joined. Note we're
|
||
|
// combining two scope types into flType here for convenience.
|
||
|
//
|
||
|
|
||
|
aScopeInit[scopeindex].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
|
||
|
aScopeInit[scopeindex].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
|
||
|
aScopeInit[scopeindex].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE |
|
||
|
DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
|
||
|
DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
|
||
|
aScopeInit[scopeindex].FilterFlags.Uplevel.flNativeModeOnly =
|
||
|
DSOP_FILTER_GLOBAL_GROUPS_SE
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_SE
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
|
||
|
| DSOP_FILTER_GLOBAL_GROUPS_DL
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_DL
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
|
||
|
| DSOP_FILTER_BUILTIN_GROUPS;
|
||
|
aScopeInit[scopeindex].FilterFlags.Uplevel.flMixedModeOnly =
|
||
|
DSOP_FILTER_GLOBAL_GROUPS_SE
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_SE
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
|
||
|
| DSOP_FILTER_GLOBAL_GROUPS_DL
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_DL
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
|
||
|
| DSOP_FILTER_BUILTIN_GROUPS;
|
||
|
aScopeInit[scopeindex].pwzDcName = bstrDC;
|
||
|
|
||
|
//
|
||
|
// Put the scope init array into the object picker init array
|
||
|
//
|
||
|
|
||
|
DSOP_INIT_INFO InitInfo;
|
||
|
ZeroMemory(&InitInfo, sizeof(InitInfo));
|
||
|
|
||
|
InitInfo.cbSize = sizeof(InitInfo);
|
||
|
|
||
|
//
|
||
|
// The pwzTargetComputer member allows the object picker to be
|
||
|
// retargetted to a different computer. It will behave as if it
|
||
|
// were being run ON THAT COMPUTER.
|
||
|
//
|
||
|
|
||
|
InitInfo.pwzTargetComputer = bstrDC;
|
||
|
InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
|
||
|
InitInfo.aDsScopeInfos = aScopeInit;
|
||
|
InitInfo.flOptions = 0;
|
||
|
|
||
|
//
|
||
|
// Note object picker makes its own copy of InitInfo. Also note
|
||
|
// that Initialize may be called multiple times, last call wins.
|
||
|
//
|
||
|
|
||
|
hr = pDsObjectPicker->Initialize(&InitInfo);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ULONG i;
|
||
|
|
||
|
for (i = 0; i < SCOPE_INIT_COUNT; i++)
|
||
|
{
|
||
|
if (FAILED(InitInfo.aDsScopeInfos[i].hr))
|
||
|
{
|
||
|
TRACE(_T("Initialization failed because of scope %u\n"), i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReportErrorEx (hwnd, IDS_OBJECT_PICKER_INIT_FAILED, hr,
|
||
|
MB_OK | MB_ICONERROR, NULL, 0);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
IDataObject *pdo = NULL;
|
||
|
//
|
||
|
// Invoke the modal dialog.
|
||
|
//
|
||
|
|
||
|
hr = pDsObjectPicker->InvokeDialog(hwnd, &pdo);
|
||
|
if (FAILED(hr))
|
||
|
return(hr);
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the user hit Cancel, hr == S_FALSE
|
||
|
//
|
||
|
if (hr == S_FALSE)
|
||
|
return hr;
|
||
|
|
||
|
bool fGotStgMedium = false;
|
||
|
|
||
|
hr = pdo->GetData(&formatetc, &stgmedium);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
fGotStgMedium = true;
|
||
|
|
||
|
PDS_SELECTION_LIST pSelList =
|
||
|
(PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
|
||
|
|
||
|
if (!pSelList)
|
||
|
{
|
||
|
TRACE(_T("GlobalLock error %u\n"), GetLastError());
|
||
|
//
|
||
|
// REVIEW_JEFFJON : should probably put some kind of error message
|
||
|
// here even though we ignore the return value
|
||
|
//
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (pDsObjectPicker)
|
||
|
{
|
||
|
pDsObjectPicker->Release();
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////
|
||
|
|
||
|
UINT index;
|
||
|
DWORD cModified = 0;
|
||
|
CString csClass;
|
||
|
CString objDN;
|
||
|
IDirectoryObject * pObj = NULL;
|
||
|
INT answer = IDYES;
|
||
|
BOOL error = FALSE;
|
||
|
BOOL partial_success = FALSE;
|
||
|
CWaitCursor CWait;
|
||
|
CString csPath;
|
||
|
|
||
|
if (pSelList != NULL)
|
||
|
{
|
||
|
TRACE(_T("AddToGroup: binding to group path is %s\n"), pSelList->aDsSelection[0].pwzADsPath);
|
||
|
CString csGroup = pSelList->aDsSelection[0].pwzADsPath;
|
||
|
RemovePortifPresent(&csGroup);
|
||
|
hr = DSAdminOpenObject(csGroup,
|
||
|
IID_IDirectoryObject,
|
||
|
(void **)&pObj,
|
||
|
FALSE /*bServer*/);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)pSelList->aDsSelection[0].pwzName};
|
||
|
ReportErrorEx (hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
goto ExitGracefully;
|
||
|
}
|
||
|
|
||
|
for (index = 0; index < pNames->GetCount(); index++)
|
||
|
{
|
||
|
csPath = pNames->GetName(index);
|
||
|
TRACE(_T("AddToGroup: object path is %s\n"), csPath);
|
||
|
csClass = pNames->GetClass(index);
|
||
|
TRACE(_T("AddToGroup: object class is %s\n"), csClass);
|
||
|
|
||
|
ADS_ATTR_INFO Attrinfo;
|
||
|
ZeroMemory (&Attrinfo, sizeof (ADS_ATTR_INFO));
|
||
|
PADS_ATTR_INFO pAttrs = &Attrinfo;
|
||
|
|
||
|
Attrinfo.pszAttrName = L"member";
|
||
|
Attrinfo.dwADsType = ADSTYPE_DN_STRING;
|
||
|
Attrinfo.dwControlCode = ADS_ATTR_APPEND;
|
||
|
ADSVALUE Value;
|
||
|
|
||
|
pAttrs->pADsValues = &Value;
|
||
|
pAttrs->dwNumValues = 1;
|
||
|
|
||
|
// make sure there's no strange escaping in the path
|
||
|
CComBSTR bstrPath;
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set((LPTSTR)(LPCTSTR)csPath, ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF);
|
||
|
pathCracker.Retrieve( ADS_FORMAT_X500, &bstrPath);
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
|
||
|
|
||
|
StripADsIPath(bstrPath, objDN);
|
||
|
|
||
|
Value.DNString = (LPWSTR)(LPCWSTR)objDN;
|
||
|
Value.dwType = ADSTYPE_DN_STRING;
|
||
|
|
||
|
hr = pObj->SetObjectAttributes(pAttrs, 1, &cModified);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
error = TRUE;
|
||
|
// prep for display by getting obj name
|
||
|
pathCracker.Set((LPWSTR)pNames->GetName(index), ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
CComBSTR ObjName;
|
||
|
pathCracker.GetElement( 0, &ObjName );
|
||
|
PVOID apv[2] = {(BSTR)(LPWSTR)(LPCWSTR)ObjName,
|
||
|
(BSTR)(LPWSTR)(LPCWSTR)pSelList->aDsSelection[0].pwzName};
|
||
|
if ((pNames->GetCount() - index) == 1)
|
||
|
{
|
||
|
ReportErrorEx (hwnd,IDS_12_MEMBER_ADD_FAILED,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int answer1;
|
||
|
answer1 = ReportErrorEx (hwnd,IDS_12_MULTI_MEMBER_ADD_FAILED,hr,
|
||
|
MB_YESNO | MB_ICONERROR, apv, 2);
|
||
|
if (answer1 == IDNO)
|
||
|
{
|
||
|
answer = IDCANCEL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
partial_success = TRUE;
|
||
|
}
|
||
|
|
||
|
if (answer == IDCANCEL)
|
||
|
{
|
||
|
goto ExitGracefully;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExitGracefully:
|
||
|
if( error )
|
||
|
{
|
||
|
if (partial_success == TRUE)
|
||
|
{
|
||
|
ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_PARTIALLY_COMPLETED, S_OK,
|
||
|
MB_OK | MB_ICONINFORMATION, NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_FAILED, S_OK,
|
||
|
MB_OK | MB_ICONINFORMATION, NULL, 0);
|
||
|
}
|
||
|
}
|
||
|
else if( partial_success == TRUE )
|
||
|
{
|
||
|
ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_COMPLETED, S_OK,
|
||
|
MB_OK | MB_ICONINFORMATION, NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//else we did nothing and appropriate messages are shown already
|
||
|
ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_FAILED, S_OK,
|
||
|
MB_OK | MB_ICONINFORMATION, NULL, 0);
|
||
|
}
|
||
|
|
||
|
if (pObj)
|
||
|
{
|
||
|
pObj->Release();
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
HRESULT AddDataObjListToGroup(IN CObjectNamesFormatCracker* pNames,
|
||
|
IN HWND hwnd,
|
||
|
IN CDSComponentData* pComponentData)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
TRACE (_T("CDSContextMenu::AddToGroup\n"));
|
||
|
|
||
|
STGMEDIUM stgmedium =
|
||
|
{
|
||
|
TYMED_HGLOBAL,
|
||
|
NULL,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
FORMATETC formatetc =
|
||
|
{
|
||
|
(CLIPFORMAT)g_cfDsObjectPicker,
|
||
|
NULL,
|
||
|
DVASPECT_CONTENT,
|
||
|
-1,
|
||
|
TYMED_HGLOBAL
|
||
|
};
|
||
|
|
||
|
// algorithm
|
||
|
// examine selection, figure out classes
|
||
|
// figure out what groups are possible
|
||
|
// call object picker, get a group
|
||
|
// for each object in selection
|
||
|
// if container
|
||
|
// procees_container()
|
||
|
// else
|
||
|
// process_leaf()
|
||
|
//
|
||
|
|
||
|
|
||
|
//
|
||
|
// Create an instance of the object picker.
|
||
|
//
|
||
|
|
||
|
IDsObjectPicker * pDsObjectPicker = NULL;
|
||
|
|
||
|
hr = CoCreateInstance(CLSID_DsObjectPicker,
|
||
|
NULL,
|
||
|
CLSCTX_INPROC_SERVER,
|
||
|
IID_IDsObjectPicker,
|
||
|
(void **) &pDsObjectPicker);
|
||
|
if (FAILED(hr))
|
||
|
return(hr);
|
||
|
//
|
||
|
// Prepare to initialize the object picker.
|
||
|
//
|
||
|
// first, get the name of DC that we are talking to.
|
||
|
CComBSTR bstrDC;
|
||
|
LPCWSTR lpszPath = pNames->GetName(0);
|
||
|
GetServerFromLDAPPath(lpszPath, &bstrDC);
|
||
|
|
||
|
//
|
||
|
// Set up the array of scope initializer structures.
|
||
|
//
|
||
|
|
||
|
static const int SCOPE_INIT_COUNT = 1;
|
||
|
DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
|
||
|
int scopeindex = 0;
|
||
|
ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// The domain to which the target computer is joined. Note we're
|
||
|
// combining two scope types into flType here for convenience.
|
||
|
//
|
||
|
|
||
|
aScopeInit[scopeindex].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
|
||
|
aScopeInit[scopeindex].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
|
||
|
aScopeInit[scopeindex].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE |
|
||
|
DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
|
||
|
DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
|
||
|
aScopeInit[scopeindex].FilterFlags.Uplevel.flNativeModeOnly =
|
||
|
DSOP_FILTER_GLOBAL_GROUPS_SE
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_SE
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
|
||
|
| DSOP_FILTER_GLOBAL_GROUPS_DL
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_DL
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
|
||
|
| DSOP_FILTER_BUILTIN_GROUPS;
|
||
|
aScopeInit[scopeindex].FilterFlags.Uplevel.flMixedModeOnly =
|
||
|
DSOP_FILTER_GLOBAL_GROUPS_SE
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
|
||
|
| DSOP_FILTER_GLOBAL_GROUPS_DL
|
||
|
| DSOP_FILTER_UNIVERSAL_GROUPS_DL
|
||
|
| DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
|
||
|
| DSOP_FILTER_BUILTIN_GROUPS;
|
||
|
aScopeInit[scopeindex].pwzDcName = bstrDC;
|
||
|
|
||
|
//
|
||
|
// Put the scope init array into the object picker init array
|
||
|
//
|
||
|
|
||
|
DSOP_INIT_INFO InitInfo;
|
||
|
ZeroMemory(&InitInfo, sizeof(InitInfo));
|
||
|
|
||
|
InitInfo.cbSize = sizeof(InitInfo);
|
||
|
|
||
|
//
|
||
|
// The pwzTargetComputer member allows the object picker to be
|
||
|
// retargetted to a different computer. It will behave as if it
|
||
|
// were being run ON THAT COMPUTER.
|
||
|
//
|
||
|
|
||
|
InitInfo.pwzTargetComputer = bstrDC;
|
||
|
InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
|
||
|
InitInfo.aDsScopeInfos = aScopeInit;
|
||
|
InitInfo.flOptions = 0;
|
||
|
|
||
|
//
|
||
|
// Note object picker makes its own copy of InitInfo. Also note
|
||
|
// that Initialize may be called multiple times, last call wins.
|
||
|
//
|
||
|
|
||
|
hr = pDsObjectPicker->Initialize(&InitInfo);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
ULONG i;
|
||
|
|
||
|
for (i = 0; i < SCOPE_INIT_COUNT; i++) {
|
||
|
if (FAILED(InitInfo.aDsScopeInfos[i].hr)) {
|
||
|
TRACE(_T("Initialization failed because of scope %u\n"), i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReportErrorEx (hwnd, IDS_OBJECT_PICKER_INIT_FAILED, hr,
|
||
|
MB_OK | MB_ICONERROR, NULL, 0);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
IDataObject *pdo = NULL;
|
||
|
//
|
||
|
// Invoke the modal dialog.
|
||
|
//
|
||
|
|
||
|
hr = pDsObjectPicker->InvokeDialog(hwnd, &pdo);
|
||
|
if (FAILED(hr))
|
||
|
return(hr);
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the user hit Cancel, hr == S_FALSE
|
||
|
//
|
||
|
if (hr == S_FALSE)
|
||
|
return hr;
|
||
|
|
||
|
bool fGotStgMedium = false;
|
||
|
|
||
|
hr = pdo->GetData(&formatetc, &stgmedium);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
fGotStgMedium = true;
|
||
|
|
||
|
PDS_SELECTION_LIST pSelList =
|
||
|
(PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
|
||
|
|
||
|
if (!pSelList)
|
||
|
{
|
||
|
TRACE(_T("GlobalLock error %u\n"), GetLastError());
|
||
|
//
|
||
|
// REVIEW_JEFFJON : should probably put some kind of error message
|
||
|
// here even though we ignore the return value
|
||
|
//
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (pDsObjectPicker) {
|
||
|
pDsObjectPicker->Release();
|
||
|
}
|
||
|
|
||
|
hr = AddDataObjListToGivenGroup(pNames,
|
||
|
pSelList->aDsSelection[0].pwzADsPath,
|
||
|
pSelList->aDsSelection[0].pwzName,
|
||
|
hwnd,
|
||
|
pComponentData);
|
||
|
|
||
|
::GlobalFree(stgmedium.hGlobal);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT AddDataObjListToGivenGroup(CObjectNamesFormatCracker * pNames,
|
||
|
LPCWSTR lpszGroupLDapPath,
|
||
|
LPCWSTR lpszGroupName,
|
||
|
HWND hwnd,
|
||
|
CDSComponentData* pComponentData)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
UINT index = 0;
|
||
|
DWORD cModified = 0;
|
||
|
CString csClass;
|
||
|
CString objDN;
|
||
|
IDirectoryObject * pObj = NULL;
|
||
|
BOOL error = FALSE;
|
||
|
BOOL partial_success = FALSE;
|
||
|
CWaitCursor CWait;
|
||
|
CString csPath;
|
||
|
|
||
|
//
|
||
|
// Prepare error structures for use with the multi-operation error dialog
|
||
|
// These arrays may not be completely full depending on the number of errors
|
||
|
// that occurr
|
||
|
//
|
||
|
HRESULT* phrArray = new HRESULT[pNames->GetCount()];
|
||
|
if (!phrArray)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
memset(phrArray, 0, pNames->GetCount() * sizeof(HRESULT));
|
||
|
|
||
|
PWSTR* pPathArray = new PWSTR[pNames->GetCount()];
|
||
|
if (!pPathArray)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
memset(pPathArray, 0, pNames->GetCount() * sizeof(PWSTR));
|
||
|
|
||
|
PWSTR* pClassArray = new PWSTR[pNames->GetCount()];
|
||
|
if (!pClassArray)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
memset(pClassArray, 0, pNames->GetCount() * sizeof(PWSTR));
|
||
|
|
||
|
UINT nErrorCount = 0;
|
||
|
|
||
|
TRACE(_T("AddToGroup: binding to group path is %s\n"), lpszGroupLDapPath);
|
||
|
CString csGroup = lpszGroupLDapPath;
|
||
|
RemovePortifPresent(&csGroup);
|
||
|
|
||
|
hr = DSAdminOpenObject(csGroup,
|
||
|
IID_IDirectoryObject,
|
||
|
(void **)&pObj,
|
||
|
FALSE /*bServer*/);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
PVOID apv[1] = {(BSTR)(LPWSTR)lpszGroupName};
|
||
|
ReportErrorEx (hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
goto ExitGracefully;
|
||
|
}
|
||
|
|
||
|
for (index = 0; index < pNames->GetCount(); index++)
|
||
|
{
|
||
|
// make sure there's no strange escaping in the path
|
||
|
CComBSTR bstrDN;
|
||
|
|
||
|
csPath = pNames->GetName(index);
|
||
|
TRACE(_T("AddToGroup: object path is %s\n"), csPath);
|
||
|
csClass = pNames->GetClass(index);
|
||
|
TRACE(_T("AddToGroup: object class is %s\n"), csClass);
|
||
|
|
||
|
ADS_ATTR_INFO Attrinfo;
|
||
|
ZeroMemory (&Attrinfo, sizeof (ADS_ATTR_INFO));
|
||
|
PADS_ATTR_INFO pAttrs = &Attrinfo;
|
||
|
|
||
|
Attrinfo.pszAttrName = L"member";
|
||
|
Attrinfo.dwADsType = ADSTYPE_DN_STRING;
|
||
|
Attrinfo.dwControlCode = ADS_ATTR_APPEND;
|
||
|
ADSVALUE Value;
|
||
|
|
||
|
pAttrs->pADsValues = &Value;
|
||
|
pAttrs->dwNumValues = 1;
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set((LPTSTR)(LPCTSTR)csPath, ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF);
|
||
|
pathCracker.Retrieve( ADS_FORMAT_X500_DN, &bstrDN);
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
|
||
|
|
||
|
objDN = bstrDN;
|
||
|
|
||
|
Value.DNString = (LPWSTR)(LPCWSTR)objDN;
|
||
|
Value.dwType = ADSTYPE_DN_STRING;
|
||
|
|
||
|
hr = pObj->SetObjectAttributes(pAttrs, 1, &cModified);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
error = TRUE;
|
||
|
|
||
|
if (pNames->GetCount() > 1)
|
||
|
{
|
||
|
if (phrArray != NULL &&
|
||
|
pPathArray != NULL &&
|
||
|
pClassArray != NULL)
|
||
|
{
|
||
|
phrArray[nErrorCount] = hr;
|
||
|
pPathArray[nErrorCount] = new WCHAR[wcslen(objDN) + 1];
|
||
|
if (pPathArray[nErrorCount] != NULL)
|
||
|
{
|
||
|
wcscpy(pPathArray[nErrorCount], objDN);
|
||
|
}
|
||
|
|
||
|
pClassArray[nErrorCount] = new WCHAR[wcslen(csClass) + 1];
|
||
|
if (pClassArray[nErrorCount] != NULL)
|
||
|
{
|
||
|
wcscpy(pClassArray[nErrorCount], csClass);
|
||
|
}
|
||
|
nErrorCount++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// prep for display by getting obj name
|
||
|
//
|
||
|
CPathCracker pathCrackerToo;
|
||
|
pathCrackerToo.Set((LPWSTR)pNames->GetName(index), ADS_SETTYPE_FULL);
|
||
|
pathCrackerToo.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
CComBSTR ObjName;
|
||
|
pathCrackerToo.GetElement( 0, &ObjName );
|
||
|
PVOID apv[2] = {(BSTR)(LPWSTR)(LPCWSTR)ObjName,
|
||
|
(BSTR)(LPWSTR)lpszGroupName};
|
||
|
ReportErrorEx (hwnd,IDS_12_MEMBER_ADD_FAILED,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 2);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
partial_success = TRUE;
|
||
|
}
|
||
|
} // for
|
||
|
|
||
|
ExitGracefully:
|
||
|
if (nErrorCount > 0 && pNames->GetCount() > 1)
|
||
|
{
|
||
|
//
|
||
|
// Load the strings for the error dialog
|
||
|
//
|
||
|
CString szTitle;
|
||
|
if (pComponentData->QuerySnapinType() == SNAPINTYPE_SITE)
|
||
|
{
|
||
|
VERIFY(szTitle.LoadString(IDS_SITESNAPINNAME));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VERIFY(szTitle.LoadString(IDS_DSSNAPINNAME));
|
||
|
}
|
||
|
|
||
|
CString szCaption;
|
||
|
VERIFY(szCaption.LoadString(IDS_MULTI_ADDTOGROUP_ERROR_CAPTION));
|
||
|
|
||
|
CString szHeader;
|
||
|
VERIFY(szHeader.LoadString(IDS_COLUMN_NAME));
|
||
|
|
||
|
CMultiselectErrorDialog errDialog(pComponentData);
|
||
|
|
||
|
VERIFY(SUCCEEDED(errDialog.Initialize(pPathArray,
|
||
|
pClassArray,
|
||
|
phrArray,
|
||
|
nErrorCount,
|
||
|
szTitle,
|
||
|
szCaption,
|
||
|
szHeader)));
|
||
|
|
||
|
errDialog.DoModal();
|
||
|
}
|
||
|
else if (nErrorCount == 0 && !error)
|
||
|
{
|
||
|
ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_COMPLETED, S_OK,
|
||
|
MB_OK | MB_ICONINFORMATION, NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Do nothing if it was single select and there was a failure
|
||
|
// The error should have already been reported.
|
||
|
//
|
||
|
}
|
||
|
|
||
|
if (pObj)
|
||
|
{
|
||
|
pObj->Release();
|
||
|
}
|
||
|
|
||
|
if (phrArray != NULL)
|
||
|
{
|
||
|
delete[] phrArray;
|
||
|
phrArray = NULL;
|
||
|
}
|
||
|
|
||
|
if (pPathArray != NULL)
|
||
|
{
|
||
|
for (UINT nIdx = 0; nIdx < pNames->GetCount(); nIdx++)
|
||
|
{
|
||
|
if (pPathArray[nIdx] != NULL)
|
||
|
{
|
||
|
delete[] pPathArray[nIdx];
|
||
|
}
|
||
|
}
|
||
|
delete[] pPathArray;
|
||
|
pPathArray = NULL;
|
||
|
}
|
||
|
|
||
|
if (pClassArray != NULL)
|
||
|
{
|
||
|
for (UINT nIdx = 0; nIdx < pNames->GetCount(); nIdx++)
|
||
|
{
|
||
|
if (pClassArray[nIdx] != NULL)
|
||
|
{
|
||
|
delete[] pClassArray[nIdx];
|
||
|
}
|
||
|
}
|
||
|
delete[] pClassArray;
|
||
|
pClassArray = NULL;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOL IsValidSiteName( LPCTSTR lpctszSiteName, BOOL* pfNonRfc )
|
||
|
{
|
||
|
if (NULL != pfNonRfc)
|
||
|
*pfNonRfc = FALSE;
|
||
|
if (NULL == lpctszSiteName)
|
||
|
return FALSE;
|
||
|
if (NULL != wcschr( lpctszSiteName, _T('.') ))
|
||
|
return FALSE;
|
||
|
DWORD dw = ::DnsValidateDnsName_W( const_cast<LPTSTR>(lpctszSiteName) );
|
||
|
switch (dw)
|
||
|
{
|
||
|
case DNS_ERROR_NON_RFC_NAME:
|
||
|
if (NULL != pfNonRfc)
|
||
|
*pfNonRfc = TRUE;
|
||
|
// fall through
|
||
|
case DNS_ERROR_RCODE_NO_ERROR:
|
||
|
break;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*******************************************************************
|
||
|
|
||
|
NAME: GetAuthenticationID
|
||
|
|
||
|
SYNOPSIS: Retrieves the UserName associated with the credentials
|
||
|
currently being used for network access.
|
||
|
(runas /netonly credentials)
|
||
|
|
||
|
RETURNS: A win32 error code
|
||
|
|
||
|
NOTE: String returned must be freed with LocalFree
|
||
|
|
||
|
HISTORY:
|
||
|
JeffreyS 05-Aug-1999 Created
|
||
|
Modified by hiteshr to return credentials
|
||
|
JeffJon 21-Nov-2000 Modified to return a win32 error
|
||
|
|
||
|
********************************************************************/
|
||
|
ULONG
|
||
|
GetAuthenticationID(LPWSTR *ppszResult)
|
||
|
{
|
||
|
ULONG ulReturn = 0;
|
||
|
HANDLE hLsa;
|
||
|
NTSTATUS Status = 0;
|
||
|
*ppszResult = NULL;
|
||
|
//
|
||
|
// These LSA calls are delay-loaded from secur32.dll using the linker's
|
||
|
// delay-load mechanism. Therefore, wrap with an exception handler.
|
||
|
//
|
||
|
__try
|
||
|
{
|
||
|
Status = LsaConnectUntrusted(&hLsa);
|
||
|
|
||
|
if (Status == 0)
|
||
|
{
|
||
|
NEGOTIATE_CALLER_NAME_REQUEST Req = {0};
|
||
|
PNEGOTIATE_CALLER_NAME_RESPONSE pResp;
|
||
|
ULONG cbSize =0;
|
||
|
NTSTATUS SubStatus =0;
|
||
|
|
||
|
Req.MessageType = NegGetCallerName;
|
||
|
|
||
|
Status = LsaCallAuthenticationPackage(
|
||
|
hLsa,
|
||
|
0,
|
||
|
&Req,
|
||
|
sizeof(Req),
|
||
|
(void**)&pResp,
|
||
|
&cbSize,
|
||
|
&SubStatus);
|
||
|
|
||
|
if ((Status == 0) && (SubStatus == 0))
|
||
|
{
|
||
|
LocalAllocString(ppszResult,pResp->CallerName);
|
||
|
LsaFreeReturnBuffer(pResp);
|
||
|
}
|
||
|
|
||
|
LsaDeregisterLogonProcess(hLsa);
|
||
|
}
|
||
|
ulReturn = LsaNtStatusToWinError(Status);
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
return ulReturn;
|
||
|
|
||
|
}
|
||
|
|
||
|
// IsLocalLogin
|
||
|
//Function determines if the user has logged in as
|
||
|
//Local user or to a domain
|
||
|
|
||
|
BOOL IsLocalLogin( void )
|
||
|
{
|
||
|
|
||
|
DWORD nSize = 0;
|
||
|
|
||
|
PWSTR pszUserName = NULL;
|
||
|
|
||
|
if (ERROR_SUCCESS != GetAuthenticationID(&pszUserName))
|
||
|
return false;
|
||
|
|
||
|
CString strSamComName( pszUserName );
|
||
|
int nPos = strSamComName.Find('\\');
|
||
|
if( -1 == nPos ){
|
||
|
LocalFree(pszUserName);
|
||
|
return false;
|
||
|
}
|
||
|
CString strDomainOrLocalName( strSamComName.Mid(0,nPos) );
|
||
|
|
||
|
WCHAR lpszName1[ MAX_COMPUTERNAME_LENGTH + 1 ];
|
||
|
nSize = MAX_COMPUTERNAME_LENGTH + 1;
|
||
|
GetComputerName( lpszName1, &nSize);
|
||
|
CString strCompName ( (LPCWSTR)lpszName1 );
|
||
|
|
||
|
LocalFree(pszUserName);
|
||
|
|
||
|
return ( strDomainOrLocalName == strCompName );
|
||
|
}
|
||
|
|
||
|
|
||
|
// IsThisUserLoggedIn
|
||
|
//Function determines if the user is the same as the
|
||
|
//passed in DN.
|
||
|
//Parameter is either a DN or a full ADSI path
|
||
|
extern LPWSTR g_lpszLoggedInUser = NULL;
|
||
|
|
||
|
BOOL IsThisUserLoggedIn( LPCTSTR pwszUserDN )
|
||
|
{
|
||
|
|
||
|
DWORD nSize = 0;
|
||
|
BOOL result = FALSE;
|
||
|
|
||
|
if (g_lpszLoggedInUser == NULL) {
|
||
|
//get the size passing null pointer
|
||
|
GetUserNameEx(NameFullyQualifiedDN , NULL, &nSize);
|
||
|
|
||
|
if( nSize == 0 )
|
||
|
return false;
|
||
|
|
||
|
g_lpszLoggedInUser = new WCHAR[ nSize ];
|
||
|
if( g_lpszLoggedInUser == NULL )
|
||
|
return false;
|
||
|
|
||
|
GetUserNameEx(NameFullyQualifiedDN, g_lpszLoggedInUser, &nSize );
|
||
|
}
|
||
|
CString csUserDN = pwszUserDN;
|
||
|
CString csDN;
|
||
|
(void) StripADsIPath(csUserDN, csDN);
|
||
|
|
||
|
if (!_wcsicmp (g_lpszLoggedInUser, csDN)) {
|
||
|
result = TRUE;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: SetSecurityInfoMask
|
||
|
//
|
||
|
// Synopsis: Reads the security descriptor from the specied DS object
|
||
|
//
|
||
|
// Arguments: [IN punk] -- IUnknown from IDirectoryObject
|
||
|
// [IN si] -- SecurityInformation
|
||
|
//// History: 25-Dec-2000 -- Hiteshr Created
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
SetSecurityInfoMask(LPUNKNOWN punk, SECURITY_INFORMATION si)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
if (punk)
|
||
|
{
|
||
|
IADsObjectOptions *pOptions;
|
||
|
hr = punk->QueryInterface(IID_IADsObjectOptions, (void**)&pOptions);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
VARIANT var;
|
||
|
VariantInit(&var);
|
||
|
V_VT(&var) = VT_I4;
|
||
|
V_I4(&var) = si;
|
||
|
hr = pOptions->SetOption(ADS_OPTION_SECURITY_MASK, var);
|
||
|
pOptions->Release();
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
WCHAR const c_szSDProperty[] = L"nTSecurityDescriptor";
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DSReadObjectSecurity
|
||
|
//
|
||
|
// Synopsis: Reads the Dacl from the specied DS object
|
||
|
//
|
||
|
// Arguments: [in pDsObject] -- IDirettoryObject for dsobject
|
||
|
// [psdControl] -- Control Setting for SD
|
||
|
// They can be returned when calling
|
||
|
// DSWriteObjectSecurity
|
||
|
// [OUT ppDacl] -- DACL returned here
|
||
|
//
|
||
|
//
|
||
|
// History 25-Oct-2000 -- hiteshr created
|
||
|
//
|
||
|
// Notes: If Object Doesn't have DACL, function will succeed but *ppDacl will
|
||
|
// be NULL.
|
||
|
// Caller must free *ppDacl, if not NULL, by calling LocalFree
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
DSReadObjectSecurity(IN IDirectoryObject *pDsObject,
|
||
|
OUT SECURITY_DESCRIPTOR_CONTROL * psdControl,
|
||
|
OUT PACL *ppDacl)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
PADS_ATTR_INFO pSDAttributeInfo = NULL;
|
||
|
|
||
|
do // false loop
|
||
|
{
|
||
|
LPWSTR pszSDProperty = (LPWSTR)c_szSDProperty;
|
||
|
DWORD dwAttributesReturned;
|
||
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
||
|
PACL pAcl = NULL;
|
||
|
|
||
|
if(!pDsObject || !ppDacl)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
hr = E_INVALIDARG;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*ppDacl = NULL;
|
||
|
|
||
|
// Set the SECURITY_INFORMATION mask
|
||
|
hr = SetSecurityInfoMask(pDsObject, DACL_SECURITY_INFORMATION);
|
||
|
if(FAILED(hr))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read the security descriptor
|
||
|
//
|
||
|
hr = pDsObject->GetObjectAttributes(&pszSDProperty,
|
||
|
1,
|
||
|
&pSDAttributeInfo,
|
||
|
&dwAttributesReturned);
|
||
|
if (SUCCEEDED(hr) && !pSDAttributeInfo)
|
||
|
hr = E_ACCESSDENIED; // This happens for SACL if no SecurityPrivilege
|
||
|
|
||
|
if(FAILED(hr))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ASSERT(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->dwADsType);
|
||
|
ASSERT(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->pADsValues->dwType);
|
||
|
|
||
|
pSD = (PSECURITY_DESCRIPTOR)pSDAttributeInfo->pADsValues->SecurityDescriptor.lpValue;
|
||
|
|
||
|
ASSERT(IsValidSecurityDescriptor(pSD));
|
||
|
|
||
|
|
||
|
//
|
||
|
//Get the security descriptor control
|
||
|
//
|
||
|
if(psdControl)
|
||
|
{
|
||
|
DWORD dwRevision;
|
||
|
if(!GetSecurityDescriptorControl(pSD, psdControl, &dwRevision))
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//Get pointer to DACL
|
||
|
//
|
||
|
BOOL bDaclPresent, bDaclDefaulted;
|
||
|
if(!GetSecurityDescriptorDacl(pSD,
|
||
|
&bDaclPresent,
|
||
|
&pAcl,
|
||
|
&bDaclDefaulted))
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(!bDaclPresent ||
|
||
|
!pAcl)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ASSERT(IsValidAcl(pAcl));
|
||
|
|
||
|
//
|
||
|
//Make a copy of the DACL
|
||
|
//
|
||
|
*ppDacl = (PACL)LocalAlloc(LPTR,pAcl->AclSize);
|
||
|
if(!*ppDacl)
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
break;
|
||
|
}
|
||
|
CopyMemory(*ppDacl,pAcl,pAcl->AclSize);
|
||
|
|
||
|
}while(0);
|
||
|
|
||
|
|
||
|
if (pSDAttributeInfo)
|
||
|
FreeADsMem(pSDAttributeInfo);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
const GUID GUID_CONTROL_UserChangePassword =
|
||
|
{ 0xab721a53, 0x1e2f, 0x11d0, { 0x98, 0x19, 0x00, 0xaa, 0x00, 0x40, 0x52, 0x9b}};
|
||
|
|
||
|
bool CanUserChangePassword(IN IDirectoryObject* pDirObject)
|
||
|
{
|
||
|
bool bCanChangePassword = false;
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
do // false loop
|
||
|
{
|
||
|
//
|
||
|
// Validate parameters
|
||
|
//
|
||
|
if (!pDirObject)
|
||
|
{
|
||
|
ASSERT(pDirObject);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
|
||
|
CSimpleAclHolder Dacl;
|
||
|
hr = DSReadObjectSecurity(pDirObject,
|
||
|
&sdControl,
|
||
|
&(Dacl.m_pAcl));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create and Initialize the Self and World SIDs
|
||
|
//
|
||
|
CSidHolder selfSid;
|
||
|
CSidHolder worldSid;
|
||
|
|
||
|
PSID pSid = NULL;
|
||
|
|
||
|
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
|
||
|
WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
|
||
|
if (!AllocateAndInitializeSid(&NtAuth,
|
||
|
1,
|
||
|
SECURITY_PRINCIPAL_SELF_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&pSid))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
selfSid.Attach(pSid, false);
|
||
|
pSid = NULL;
|
||
|
|
||
|
if (!AllocateAndInitializeSid(&WorldAuth,
|
||
|
1,
|
||
|
SECURITY_WORLD_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&pSid))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
worldSid.Attach(pSid, false);
|
||
|
pSid = NULL;
|
||
|
|
||
|
ULONG ulCount = 0, j = 0;
|
||
|
PEXPLICIT_ACCESS rgEntries = NULL;
|
||
|
|
||
|
DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
|
||
|
|
||
|
if (ERROR_SUCCESS != dwErr)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Are these ACEs already present?
|
||
|
//
|
||
|
bool bSelfAllowPresent = false;
|
||
|
bool bWorldAllowPresent = false;
|
||
|
bool bSelfDenyPresent = false;
|
||
|
bool bWorldDenyPresent = false;
|
||
|
|
||
|
//
|
||
|
// Loop through looking for the can change password ACE for self and world
|
||
|
//
|
||
|
for (j = 0; j < ulCount; j++)
|
||
|
{
|
||
|
//
|
||
|
// Look for deny ACEs
|
||
|
//
|
||
|
if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
|
||
|
(rgEntries[j].grfAccessMode == DENY_ACCESS))
|
||
|
{
|
||
|
OBJECTS_AND_SID* pObjectsAndSid = NULL;
|
||
|
pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
|
||
|
|
||
|
//
|
||
|
// Look for the user can change password ACE
|
||
|
//
|
||
|
if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
|
||
|
GUID_CONTROL_UserChangePassword))
|
||
|
{
|
||
|
//
|
||
|
// See if it is for the self SID or the world SID
|
||
|
//
|
||
|
if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
|
||
|
{
|
||
|
//
|
||
|
// Deny self found
|
||
|
//
|
||
|
bSelfDenyPresent = true;
|
||
|
}
|
||
|
else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
|
||
|
{
|
||
|
//
|
||
|
// Deny world found
|
||
|
//
|
||
|
bWorldDenyPresent = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Look for allow ACEs
|
||
|
//
|
||
|
else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
|
||
|
(rgEntries[j].grfAccessMode == GRANT_ACCESS))
|
||
|
{
|
||
|
OBJECTS_AND_SID* pObjectsAndSid = NULL;
|
||
|
pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
|
||
|
|
||
|
//
|
||
|
// Look for the user can change password ACE
|
||
|
//
|
||
|
if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
|
||
|
GUID_CONTROL_UserChangePassword))
|
||
|
{
|
||
|
//
|
||
|
// See if it is for the self SID or the world SID
|
||
|
//
|
||
|
if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
|
||
|
{
|
||
|
//
|
||
|
// Allow self found
|
||
|
//
|
||
|
bSelfAllowPresent = true;
|
||
|
}
|
||
|
else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
|
||
|
{
|
||
|
//
|
||
|
// Allow world found
|
||
|
//
|
||
|
bWorldAllowPresent = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bSelfDenyPresent || bWorldDenyPresent)
|
||
|
{
|
||
|
//
|
||
|
// There is an explicit deny so we know that the user cannot change password
|
||
|
//
|
||
|
bCanChangePassword = false;
|
||
|
}
|
||
|
else if ((!bSelfDenyPresent && !bWorldDenyPresent) &&
|
||
|
(bSelfAllowPresent || bWorldAllowPresent))
|
||
|
{
|
||
|
//
|
||
|
// There is no explicit deny but there are explicit allows so we know that
|
||
|
// the user can change password
|
||
|
//
|
||
|
bCanChangePassword = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// We are not sure because the explicit entries are not telling us for
|
||
|
// certain so it all depends on inheritence. Most likely they will
|
||
|
// be able to change their password unless the admin has changed something
|
||
|
// higher up or through group membership
|
||
|
//
|
||
|
bCanChangePassword = true;
|
||
|
}
|
||
|
} while(false);
|
||
|
|
||
|
return bCanChangePassword;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////
|
||
|
// CDSNotifyDataObject
|
||
|
|
||
|
class CDSNotifyDataObject : public IDataObject, public CComObjectRoot
|
||
|
{
|
||
|
// ATL Maps
|
||
|
DECLARE_NOT_AGGREGATABLE(CDSNotifyDataObject)
|
||
|
BEGIN_COM_MAP(CDSNotifyDataObject)
|
||
|
COM_INTERFACE_ENTRY(IDataObject)
|
||
|
END_COM_MAP()
|
||
|
|
||
|
// Construction/Destruction
|
||
|
CDSNotifyDataObject()
|
||
|
{
|
||
|
m_dwFlags = 0;
|
||
|
m_dwProviderFlags = 0;
|
||
|
m_pCLSIDNamespace = NULL;
|
||
|
}
|
||
|
~CDSNotifyDataObject() {}
|
||
|
|
||
|
// Standard IDataObject methods
|
||
|
public:
|
||
|
// Implemented
|
||
|
STDMETHOD(GetData)(FORMATETC * pformatetcIn, STGMEDIUM * pmedium);
|
||
|
|
||
|
// Not Implemented
|
||
|
private:
|
||
|
STDMETHOD(GetDataHere)(FORMATETC*, STGMEDIUM*) { return E_NOTIMPL; };
|
||
|
STDMETHOD(EnumFormatEtc)(DWORD, IEnumFORMATETC**) { return E_NOTIMPL; };
|
||
|
STDMETHOD(SetData)(FORMATETC*, STGMEDIUM*, BOOL) { return E_NOTIMPL; };
|
||
|
STDMETHOD(QueryGetData)(FORMATETC*) { return E_NOTIMPL; };
|
||
|
STDMETHOD(GetCanonicalFormatEtc)(FORMATETC*, FORMATETC*) { return E_NOTIMPL; };
|
||
|
STDMETHOD(DAdvise)(FORMATETC*, DWORD, IAdviseSink*, DWORD*) { return E_NOTIMPL; };
|
||
|
STDMETHOD(DUnadvise)(DWORD) { return E_NOTIMPL; };
|
||
|
STDMETHOD(EnumDAdvise)(IEnumSTATDATA**) { return E_NOTIMPL; };
|
||
|
|
||
|
public:
|
||
|
// Property Page Clipboard formats
|
||
|
static CLIPFORMAT m_cfDsObjectNames;
|
||
|
|
||
|
// initialization
|
||
|
HRESULT Init(LPCWSTR lpszPath, LPCWSTR lpszClass, BOOL bContainer,
|
||
|
CDSComponentData* pCD);
|
||
|
// Implementation
|
||
|
private:
|
||
|
CString m_szPath;
|
||
|
CString m_szClass;
|
||
|
DWORD m_dwFlags;
|
||
|
DWORD m_dwProviderFlags;
|
||
|
const CLSID* m_pCLSIDNamespace;
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
CLIPFORMAT CDSNotifyDataObject::m_cfDsObjectNames =
|
||
|
(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
|
||
|
|
||
|
|
||
|
STDMETHODIMP CDSNotifyDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
|
||
|
{
|
||
|
if ((pFormatEtc == NULL) || (pMedium == NULL))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
|
||
|
{
|
||
|
return DV_E_TYMED;
|
||
|
}
|
||
|
|
||
|
// we support only one clipboard format
|
||
|
pMedium->tymed = TYMED_HGLOBAL;
|
||
|
pMedium->pUnkForRelease = NULL;
|
||
|
if (pFormatEtc->cfFormat != m_cfDsObjectNames)
|
||
|
{
|
||
|
return DV_E_FORMATETC;
|
||
|
}
|
||
|
|
||
|
// figure out how much storage we need
|
||
|
int nPathLen = m_szPath.GetLength();
|
||
|
int nClassLen = m_szClass.GetLength();
|
||
|
int cbStruct = sizeof(DSOBJECTNAMES); //contains already a DSOBJECT embedded struct
|
||
|
DWORD cbStorage = (nPathLen + 1 + nClassLen + 1) * sizeof(WCHAR);
|
||
|
|
||
|
LPDSOBJECTNAMES pDSObj;
|
||
|
|
||
|
pDSObj = (LPDSOBJECTNAMES)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
|
||
|
cbStruct + cbStorage);
|
||
|
|
||
|
if (pDSObj == NULL)
|
||
|
{
|
||
|
return STG_E_MEDIUMFULL;
|
||
|
}
|
||
|
|
||
|
// write the info
|
||
|
pDSObj->clsidNamespace = *m_pCLSIDNamespace;
|
||
|
pDSObj->cItems = 1;
|
||
|
|
||
|
pDSObj->aObjects[0].dwFlags = m_dwFlags;
|
||
|
pDSObj->aObjects[0].dwProviderFlags = m_dwProviderFlags;
|
||
|
|
||
|
pDSObj->aObjects[0].offsetName = cbStruct;
|
||
|
pDSObj->aObjects[0].offsetClass = cbStruct + (nPathLen + 1) * sizeof(WCHAR);
|
||
|
|
||
|
_tcscpy((LPTSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetName)), (LPCWSTR)m_szPath);
|
||
|
|
||
|
_tcscpy((LPTSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetClass)), (LPCWSTR)m_szClass);
|
||
|
|
||
|
pMedium->hGlobal = (HGLOBAL)pDSObj;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CDSNotifyDataObject::Init(LPCWSTR lpszPath, LPCWSTR lpszClass, BOOL bContainer,
|
||
|
CDSComponentData* pCD)
|
||
|
{
|
||
|
m_szPath = lpszPath;
|
||
|
m_szClass = lpszClass;
|
||
|
|
||
|
switch (pCD->QuerySnapinType())
|
||
|
{
|
||
|
case SNAPINTYPE_DS:
|
||
|
m_pCLSIDNamespace = &CLSID_DSSnapin;
|
||
|
break;
|
||
|
case SNAPINTYPE_SITE:
|
||
|
m_pCLSIDNamespace = &CLSID_SiteSnapin;
|
||
|
break;
|
||
|
default:
|
||
|
m_pCLSIDNamespace = &CLSID_NULL;
|
||
|
}
|
||
|
|
||
|
m_dwProviderFlags = (pCD->IsAdvancedView()) ? DSPROVIDER_ADVANCED : 0;
|
||
|
|
||
|
m_dwFlags = bContainer ? DSOBJECT_ISCONTAINER : 0;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CChangePasswordPrivilegeAction
|
||
|
|
||
|
static GUID UserChangePasswordGUID =
|
||
|
{ 0xab721a53, 0x1e2f, 0x11d0, { 0x98, 0x19, 0x00, 0xaa, 0x00, 0x40, 0x52, 0x9b}};
|
||
|
|
||
|
|
||
|
HRESULT CChangePasswordPrivilegeAction::Load(IADs * pIADs)
|
||
|
{
|
||
|
// reset state, just in case
|
||
|
m_bstrObjectLdapPath = (LPCWSTR)NULL;
|
||
|
m_pDacl = NULL;
|
||
|
m_SelfSid.Clear();
|
||
|
m_WorldSid.Clear();
|
||
|
|
||
|
// get the full LDAP path of the object
|
||
|
HRESULT hr = pIADs->get_ADsPath(&m_bstrObjectLdapPath);
|
||
|
ASSERT (SUCCEEDED(hr));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TRACE(_T("failed on pIADs->get_ADsPath()\n"));
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
UnescapePath(m_bstrObjectLdapPath, /*bDN*/ FALSE, m_bstrObjectLdapPath);
|
||
|
|
||
|
// allocate SIDs
|
||
|
hr = _SetSids();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TRACE(_T("failed on _SetSids()\n"));
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// read info
|
||
|
TRACE(_T("GetNamedSecurityInfo(%s)\n"), m_bstrObjectLdapPath);
|
||
|
|
||
|
DWORD dwErr = ::GetNamedSecurityInfo(
|
||
|
IN m_bstrObjectLdapPath,
|
||
|
IN SE_DS_OBJECT_ALL,
|
||
|
IN DACL_SECURITY_INFORMATION,
|
||
|
OUT NULL,
|
||
|
OUT NULL,
|
||
|
OUT &(m_pDacl),
|
||
|
OUT NULL,
|
||
|
OUT &(m_SDHolder.m_pSD));
|
||
|
|
||
|
TRACE(L"GetNamedSecurityInfo() returned dwErr = 0x%x\n", dwErr);
|
||
|
|
||
|
if (dwErr != ERROR_SUCCESS)
|
||
|
{
|
||
|
TRACE(L"GetNamedSecurityInfo() failed!\n");
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CChangePasswordPrivilegeAction::Read(BOOL* pbPasswordCannotChange)
|
||
|
{
|
||
|
*pbPasswordCannotChange = FALSE;
|
||
|
// find about the existence of the deny ACEs
|
||
|
|
||
|
ULONG ulCount, j;
|
||
|
PEXPLICIT_ACCESS rgEntries;
|
||
|
|
||
|
ASSERT(m_pDacl);
|
||
|
DWORD dwErr = GetExplicitEntriesFromAcl(m_pDacl, &ulCount, &rgEntries);
|
||
|
TRACE(L"GetExplicitEntriesFromAcl() returned dwErr = 0x%x\n", dwErr);
|
||
|
|
||
|
if (dwErr != ERROR_SUCCESS)
|
||
|
{
|
||
|
TRACE(L"GetExplicitEntriesFromAcl() failed!\n");
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < ulCount; j++)
|
||
|
{
|
||
|
if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
|
||
|
(rgEntries[j].grfAccessMode == DENY_ACCESS))
|
||
|
{
|
||
|
OBJECTS_AND_SID * pObjectsAndSid;
|
||
|
pObjectsAndSid = (OBJECTS_AND_SID *)rgEntries[j].Trustee.ptstrName;
|
||
|
|
||
|
if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
|
||
|
UserChangePasswordGUID) &&
|
||
|
(EqualSid(pObjectsAndSid->pSid, m_SelfSid.Get()) ||
|
||
|
EqualSid(pObjectsAndSid->pSid, m_WorldSid.Get())))
|
||
|
{
|
||
|
*pbPasswordCannotChange = TRUE;
|
||
|
} // if
|
||
|
} // if
|
||
|
} // for
|
||
|
|
||
|
TRACE(L"*pbPasswordCannotChange = %d\n", *pbPasswordCannotChange);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CChangePasswordPrivilegeAction::Revoke()
|
||
|
{
|
||
|
DWORD dwErr = 0;
|
||
|
|
||
|
EXPLICIT_ACCESS rgAccessEntry[2] = {0};
|
||
|
OBJECTS_AND_SID rgObjectsAndSid[2] = {0};
|
||
|
// initialize the entries (DENY ACE's)
|
||
|
rgAccessEntry[0].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
|
||
|
rgAccessEntry[0].grfAccessMode = DENY_ACCESS;
|
||
|
rgAccessEntry[0].grfInheritance = NO_INHERITANCE;
|
||
|
|
||
|
rgAccessEntry[1].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
|
||
|
rgAccessEntry[1].grfAccessMode = DENY_ACCESS;
|
||
|
rgAccessEntry[1].grfInheritance = NO_INHERITANCE;
|
||
|
|
||
|
|
||
|
// build the trustee structs for change password
|
||
|
BuildTrusteeWithObjectsAndSid(&(rgAccessEntry[0].Trustee),
|
||
|
&(rgObjectsAndSid[0]),
|
||
|
&UserChangePasswordGUID,
|
||
|
NULL, // inherit guid
|
||
|
m_SelfSid.Get()
|
||
|
);
|
||
|
|
||
|
BuildTrusteeWithObjectsAndSid(&(rgAccessEntry[1].Trustee),
|
||
|
&(rgObjectsAndSid[1]),
|
||
|
&UserChangePasswordGUID,
|
||
|
NULL, // inherit guid
|
||
|
m_WorldSid.Get()
|
||
|
);
|
||
|
|
||
|
// add the entries to the ACL
|
||
|
TRACE(L"calling SetEntriesInAcl()\n");
|
||
|
|
||
|
CSimpleAclHolder NewDacl;
|
||
|
dwErr = ::SetEntriesInAcl(2, rgAccessEntry, m_pDacl, &(NewDacl.m_pAcl));
|
||
|
|
||
|
TRACE(L"SetEntriesInAcl() returned dwErr = 0x%x\n", dwErr);
|
||
|
|
||
|
if (dwErr != ERROR_SUCCESS)
|
||
|
{
|
||
|
TRACE(_T("SetEntriesInAccessList failed!\n"));
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
|
||
|
// commit the changes
|
||
|
TRACE(L"calling SetNamedSecurityInfo()\n");
|
||
|
|
||
|
dwErr = ::SetNamedSecurityInfo(
|
||
|
IN m_bstrObjectLdapPath,
|
||
|
IN SE_DS_OBJECT_ALL,
|
||
|
IN DACL_SECURITY_INFORMATION,
|
||
|
IN NULL,
|
||
|
IN NULL,
|
||
|
IN NewDacl.m_pAcl,
|
||
|
IN NULL);
|
||
|
|
||
|
TRACE(L"SetNamedSecurityInfo() returned dwErr = 0x%x\n", dwErr);
|
||
|
|
||
|
if (dwErr != ERROR_SUCCESS)
|
||
|
{
|
||
|
TRACE(_T("SetNamedSecurityInfo() failed!\n"));
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CChangePasswordPrivilegeAction::_SetSids()
|
||
|
{
|
||
|
PSID pSidTemp;
|
||
|
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
|
||
|
WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
|
||
|
//
|
||
|
// build SID's for Self and World.
|
||
|
//
|
||
|
if (!AllocateAndInitializeSid(&NtAuth,
|
||
|
1,
|
||
|
SECURITY_PRINCIPAL_SELF_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&pSidTemp))
|
||
|
{
|
||
|
TRACE(_T("AllocateAndInitializeSid failed!\n"));
|
||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||
|
}
|
||
|
m_SelfSid.Attach(pSidTemp, FALSE);
|
||
|
|
||
|
if (!AllocateAndInitializeSid(&WorldAuth,
|
||
|
1,
|
||
|
SECURITY_WORLD_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&pSidTemp))
|
||
|
{
|
||
|
TRACE(_T("AllocateAndInitializeSid failed!\n"));
|
||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||
|
}
|
||
|
m_WorldSid.Attach(pSidTemp, FALSE);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CDSNotifyHandlerTransaction
|
||
|
|
||
|
CDSNotifyHandlerTransaction::CDSNotifyHandlerTransaction(CDSComponentData* pCD)
|
||
|
{
|
||
|
m_bStarted = FALSE;
|
||
|
m_uEvent = 0;
|
||
|
|
||
|
m_pCD = pCD;
|
||
|
if (m_pCD != NULL)
|
||
|
{
|
||
|
m_pMgr = pCD->GetNotifyHandlerManager();
|
||
|
ASSERT(m_pMgr != NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UINT CDSNotifyHandlerTransaction::NeedNotifyCount()
|
||
|
{
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
return 0; // we have no handler for doing notifications
|
||
|
|
||
|
if (!m_pMgr->HasHandlers())
|
||
|
return 0;
|
||
|
return m_pMgr->NeedNotifyCount(m_uEvent);
|
||
|
}
|
||
|
|
||
|
void CDSNotifyHandlerTransaction::SetCheckListBox(CCheckListBox* pCheckListBox)
|
||
|
{
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
return; // we have no handler for doing notifications
|
||
|
|
||
|
ASSERT(m_pMgr->HasHandlers());
|
||
|
m_pMgr->SetCheckListBox(pCheckListBox, m_uEvent);
|
||
|
}
|
||
|
|
||
|
void CDSNotifyHandlerTransaction::ReadFromCheckListBox(CCheckListBox* pCheckListBox)
|
||
|
{
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
return; // we have no handler for doing notifications
|
||
|
|
||
|
ASSERT(m_pMgr->HasHandlers());
|
||
|
m_pMgr->ReadFromCheckListBox(pCheckListBox, m_uEvent);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CDSNotifyHandlerTransaction::Begin(LPCWSTR lpszArg1Path,
|
||
|
LPCWSTR lpszArg1Class,
|
||
|
BOOL bArg1Cont,
|
||
|
LPCWSTR lpszArg2Path,
|
||
|
LPCWSTR lpszArg2Class,
|
||
|
BOOL bArg2Cont)
|
||
|
{
|
||
|
m_bStarted = TRUE;
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
m_spArg1 = NULL;
|
||
|
m_spArg2 = NULL;
|
||
|
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
{
|
||
|
return S_OK; // we have no handler for doing notifications
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if needed, do delayed message handler initialization
|
||
|
//
|
||
|
m_pMgr->Load(m_pCD->GetBasePathsInfo());
|
||
|
|
||
|
//
|
||
|
// avoid building data objects if there are no handlers available
|
||
|
//
|
||
|
if (!m_pMgr->HasHandlers())
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// build first argument data object
|
||
|
//
|
||
|
HRESULT hr = _BuildDataObject(lpszArg1Path, lpszArg1Class, bArg1Cont, &m_spArg1);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if needed, build second argument
|
||
|
//
|
||
|
if ( (m_uEvent == DSA_NOTIFY_MOV) || (m_uEvent == DSA_NOTIFY_REN) )
|
||
|
{
|
||
|
hr = _BuildDataObject(lpszArg2Path, lpszArg2Class, bArg2Cont, &m_spArg2);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_pMgr->Begin(m_uEvent, m_spArg1, m_spArg2);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CDSNotifyHandlerTransaction::Begin(CDSCookie* pArg1Cookie,
|
||
|
LPCWSTR lpszArg2Path,
|
||
|
LPCWSTR lpszArg2Class,
|
||
|
BOOL bArg2Cont)
|
||
|
{
|
||
|
m_bStarted = TRUE;
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
{
|
||
|
return S_OK; // we have no handler for doing notifications
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// get info from the node and cookie and call the other Begin() function
|
||
|
//
|
||
|
CString szPath;
|
||
|
m_pCD->GetBasePathsInfo()->ComposeADsIPath(szPath, pArg1Cookie->GetPath());
|
||
|
|
||
|
return Begin(szPath, pArg1Cookie->GetClass(), pArg1Cookie->IsContainerClass(),
|
||
|
lpszArg2Path, lpszArg2Class, bArg2Cont);
|
||
|
}
|
||
|
|
||
|
HRESULT CDSNotifyHandlerTransaction::Begin(IDataObject* pArg1,
|
||
|
LPCWSTR lpszArg2Path,
|
||
|
LPCWSTR lpszArg2Class,
|
||
|
BOOL bArg2Cont)
|
||
|
{
|
||
|
m_bStarted = TRUE;
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
m_spArg1 = NULL;
|
||
|
m_spArg2 = NULL;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL))
|
||
|
{
|
||
|
return S_OK; // we have no handler for doing notifications
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if needed, do delayed message handler initialization
|
||
|
//
|
||
|
m_pMgr->Load(m_pCD->GetBasePathsInfo());
|
||
|
|
||
|
//
|
||
|
// avoid building data objects if there are no handlers available
|
||
|
//
|
||
|
if (!m_pMgr->HasHandlers())
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// get the first argument as is
|
||
|
//
|
||
|
m_spArg1 = pArg1;
|
||
|
|
||
|
//
|
||
|
// if needed, build second argument
|
||
|
//
|
||
|
if ( (m_uEvent != DSA_NOTIFY_DEL) && (m_uEvent != DSA_NOTIFY_PROP) )
|
||
|
{
|
||
|
hr = _BuildDataObject(lpszArg2Path, lpszArg2Class, bArg2Cont, &m_spArg2);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_pMgr->Begin(m_uEvent, m_spArg1, m_spArg2);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
void CDSNotifyHandlerTransaction::Notify(ULONG nItem)
|
||
|
{
|
||
|
ASSERT(m_bStarted);
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL) || !m_pMgr->HasHandlers())
|
||
|
return;
|
||
|
|
||
|
m_pMgr->Notify(nItem, m_uEvent);
|
||
|
}
|
||
|
|
||
|
|
||
|
void CDSNotifyHandlerTransaction::End()
|
||
|
{
|
||
|
ASSERT(m_bStarted);
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
if ((m_pCD == NULL) || (m_pMgr == NULL) || !m_pMgr->HasHandlers())
|
||
|
return;
|
||
|
|
||
|
m_pMgr->End(m_uEvent);
|
||
|
m_spArg1 = NULL;
|
||
|
m_spArg2 = NULL;
|
||
|
m_bStarted = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CDSNotifyHandlerTransaction::BuildTransactionDataObject(LPCWSTR lpszArgPath,
|
||
|
LPCWSTR lpszArgClass,
|
||
|
BOOL bContainer,
|
||
|
CDSComponentData* pCD,
|
||
|
IDataObject** ppArg)
|
||
|
{
|
||
|
|
||
|
(*ppArg) = NULL;
|
||
|
ASSERT((lpszArgPath != NULL) && (lpszArgPath[0] != NULL));
|
||
|
ASSERT((lpszArgClass != NULL) && (lpszArgClass[0] != NULL));
|
||
|
|
||
|
//
|
||
|
// need to build a data object and hang on to it
|
||
|
//
|
||
|
CComObject<CDSNotifyDataObject>* pObject;
|
||
|
|
||
|
CComObject<CDSNotifyDataObject>::CreateInstance(&pObject);
|
||
|
if (pObject == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT hr = pObject->FinalConstruct();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pObject;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
hr = pObject->Init(lpszArgPath, lpszArgClass, bContainer, pCD);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pObject;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
hr = pObject->QueryInterface(IID_IDataObject,
|
||
|
reinterpret_cast<void**>(ppArg));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
//
|
||
|
// delete object by calling Release()
|
||
|
//
|
||
|
(*ppArg)->Release();
|
||
|
(*ppArg) = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CDSNotifyHandlerTransaction::_BuildDataObject(LPCWSTR lpszArgPath,
|
||
|
LPCWSTR lpszArgClass,
|
||
|
BOOL bContainer,
|
||
|
IDataObject** ppArg)
|
||
|
{
|
||
|
ASSERT(m_uEvent != 0);
|
||
|
ASSERT(m_pCD != NULL);
|
||
|
return BuildTransactionDataObject(lpszArgPath, lpszArgClass,bContainer, m_pCD, ppArg);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
// JonN 6/2/00 99382
|
||
|
// SITEREPL: Run interference when administrator attempts to
|
||
|
// delete critical object (NTDS Settings)
|
||
|
// reports own errors, returns true iff deletion should proceed
|
||
|
bool CUIOperationHandlerBase::CheckForNTDSDSAInSubtree(
|
||
|
LPCTSTR lpszX500Path,
|
||
|
LPCTSTR lpszItemName)
|
||
|
{
|
||
|
if (NULL == GetComponentData())
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// set up subtree search
|
||
|
CDSSearch Search(GetComponentData()->GetClassCache(), GetComponentData());
|
||
|
CString strRootPath;
|
||
|
GetComponentData()->GetBasePathsInfo()->ComposeADsIPath(strRootPath, lpszX500Path);
|
||
|
HRESULT hr = Search.Init(strRootPath);
|
||
|
|
||
|
// retrieve X500DN path to schema container
|
||
|
CString strSchemaPath;
|
||
|
GetComponentData()->GetBasePathsInfo()->GetSchemaPath(strSchemaPath);
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strSchemaPath),
|
||
|
ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
CComBSTR sbstrSchemaPathX500DN;
|
||
|
pathCracker.Retrieve(ADS_FORMAT_X500_DN,&sbstrSchemaPathX500DN);
|
||
|
|
||
|
// filter search
|
||
|
CString strFilter;
|
||
|
strFilter = L"(|(objectCategory=CN=NTDS-DSA,";
|
||
|
strFilter += sbstrSchemaPathX500DN;
|
||
|
strFilter += L")(&(objectCategory=CN=Computer,";
|
||
|
strFilter += sbstrSchemaPathX500DN;
|
||
|
strFilter += L")(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND_W L":=8192)))";
|
||
|
Search.SetFilterString((LPWSTR)(LPCWSTR)strFilter);
|
||
|
|
||
|
Search.SetSearchScope(ADS_SCOPE_SUBTREE);
|
||
|
|
||
|
LPWSTR pAttrs[2] = {L"objectClass",
|
||
|
L"distinguishedName"};
|
||
|
Search.SetAttributeList (pAttrs, 2);
|
||
|
hr = Search.DoQuery();
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = Search.GetNextRow();
|
||
|
CString strX500PathDC;
|
||
|
while (SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS)
|
||
|
{
|
||
|
ADS_SEARCH_COLUMN Column;
|
||
|
hr = Search.GetColumn (pAttrs[1], &Column);
|
||
|
if (FAILED(hr) || Column.dwNumValues < 1) break;
|
||
|
strX500PathDC = Column.pADsValues[Column.dwNumValues-1].CaseIgnoreString;
|
||
|
Search.FreeColumn(&Column);
|
||
|
if (lstrcmpi(lpszX500Path,strX500PathDC))
|
||
|
break;
|
||
|
|
||
|
// This is the object being deleted, this check does not apply here.
|
||
|
// Continue the search.
|
||
|
hr = Search.GetNextRow();
|
||
|
}
|
||
|
|
||
|
if (hr == S_ADS_NOMORE_ROWS)
|
||
|
return true;
|
||
|
else if (FAILED(hr))
|
||
|
return true; // CODEWORK do we want to allow this operation to proceed?
|
||
|
|
||
|
// retrieve the name of the DC
|
||
|
CComBSTR sbstrDCName;
|
||
|
bool fFoundNTDSDSA = FALSE;
|
||
|
ADS_SEARCH_COLUMN Column;
|
||
|
hr = Search.GetColumn (pAttrs[0], &Column);
|
||
|
if (SUCCEEDED(hr) && Column.dwNumValues > 0)
|
||
|
{
|
||
|
fFoundNTDSDSA = !lstrcmpi( L"nTDSDSA",
|
||
|
Column.pADsValues[Column.dwNumValues-1].CaseIgnoreString );
|
||
|
Search.FreeColumn(&Column);
|
||
|
pathCracker.Set((LPWSTR)(LPCWSTR)strX500PathDC, ADS_SETTYPE_DN);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
pathCracker.GetElement( (fFoundNTDSDSA)?1:0, &sbstrDCName );
|
||
|
}
|
||
|
|
||
|
|
||
|
// display an error message
|
||
|
PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPCTSTR)sbstrDCName};
|
||
|
(void) ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINS_DC,hr,
|
||
|
MB_ICONERROR, apv, 2);
|
||
|
|
||
|
// do not proceed with subtree deletion
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
*/
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
// CheckForCriticalSystemObjectInSubtree
|
||
|
//
|
||
|
// description:
|
||
|
// This function does a subtree search of the container that is passed in looking
|
||
|
// for all objects that have isCriticalSystemObject=TRUE or NTDS Settings objects.
|
||
|
//
|
||
|
// parameters:
|
||
|
// lpszX500Path - (IN) the X500 path of the container in which to search for
|
||
|
// critical system objects
|
||
|
// lpszItemName - (IN) the displayable name of the container in which to search
|
||
|
// for critical system objects
|
||
|
//
|
||
|
// return value:
|
||
|
// true - The container does not contain any critical system objects
|
||
|
// false - The container does contain at least on critical system object
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
bool CUIOperationHandlerBase::CheckForCriticalSystemObjectInSubtree(
|
||
|
LPCTSTR lpszX500Path,
|
||
|
LPCTSTR lpszItemName)
|
||
|
{
|
||
|
bool bRet = true;
|
||
|
|
||
|
if (NULL == GetComponentData())
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
CString szCriticalObjPath;
|
||
|
|
||
|
// set up subtree search
|
||
|
CDSSearch Search(GetComponentData()->GetClassCache(), GetComponentData());
|
||
|
HRESULT hr = Search.Init(lpszX500Path);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ASSERT(FALSE && L"Failed to set the path in the search object");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// retrieve X500DN path to schema container
|
||
|
//
|
||
|
CString strSchemaPath;
|
||
|
GetComponentData()->GetBasePathsInfo()->GetSchemaPath(strSchemaPath);
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strSchemaPath),
|
||
|
ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
CComBSTR sbstrSchemaPathX500DN;
|
||
|
pathCracker.Retrieve(ADS_FORMAT_X500_DN,&sbstrSchemaPathX500DN);
|
||
|
|
||
|
//
|
||
|
// filter search
|
||
|
//
|
||
|
CString strFilter;
|
||
|
strFilter = L"(|(&(objectClass=*)(isCriticalSystemObject=TRUE))";
|
||
|
strFilter += L"(|(objectCategory=CN=NTDS-DSA,";
|
||
|
strFilter += sbstrSchemaPathX500DN;
|
||
|
|
||
|
//
|
||
|
// 212232 JonN 10/27/00 Protect interSiteTransport objects
|
||
|
//
|
||
|
strFilter += L")(objectCategory=CN=Inter-Site-Transport,";
|
||
|
strFilter += sbstrSchemaPathX500DN;
|
||
|
|
||
|
strFilter += L")(&(objectCategory=CN=Computer,";
|
||
|
strFilter += sbstrSchemaPathX500DN;
|
||
|
strFilter += L")(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND_W L":=8192))))";
|
||
|
|
||
|
Search.SetFilterString((LPWSTR)(LPCWSTR)strFilter);
|
||
|
|
||
|
Search.SetSearchScope(ADS_SCOPE_SUBTREE);
|
||
|
|
||
|
LPWSTR pAttrs[4] = { L"aDSPath",
|
||
|
L"objectClass",
|
||
|
L"distinguishedName",
|
||
|
L"isCriticalSystemObject"};
|
||
|
Search.SetAttributeList (pAttrs, 4);
|
||
|
hr = Search.DoQuery();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = Search.GetNextRow();
|
||
|
}
|
||
|
|
||
|
if (hr == S_ADS_NOMORE_ROWS)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
while (SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS)
|
||
|
{
|
||
|
ADS_SEARCH_COLUMN PathColumn, CriticalColumn;
|
||
|
|
||
|
hr = Search.GetColumn(pAttrs[3], &CriticalColumn);
|
||
|
if (SUCCEEDED(hr) && CriticalColumn.pADsValues != NULL && CriticalColumn.pADsValues->Boolean)
|
||
|
{
|
||
|
//
|
||
|
// We found a critical system object so report the error and then return
|
||
|
//
|
||
|
hr = Search.GetColumn(pAttrs[0], &PathColumn);
|
||
|
|
||
|
if (SUCCEEDED(hr) && PathColumn.dwNumValues == 1 && PathColumn.pADsValues != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Get the DN as a Windows style path
|
||
|
//
|
||
|
CComBSTR bstrLeaf;
|
||
|
CPathCracker pathCrackerToo;
|
||
|
HRESULT hrPathCracker = pathCrackerToo.Set(PathColumn.pADsValues->CaseIgnoreString, ADS_SETTYPE_FULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hrPathCracker = pathCrackerToo.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hrPathCracker = pathCrackerToo.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hrPathCracker = pathCrackerToo.Retrieve(ADS_FORMAT_LEAF, &bstrLeaf);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// display an error message
|
||
|
//
|
||
|
if (wcslen(bstrLeaf))
|
||
|
{
|
||
|
PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPWSTR)bstrLeaf };
|
||
|
(void) ReportErrorEx(GetParentHwnd(),IDS_CONTAINS_CRITICALSYSOBJ,S_OK,
|
||
|
MB_ICONERROR, apv, 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)PathColumn.pADsValues->CaseIgnoreString };
|
||
|
(void) ReportErrorEx(GetParentHwnd(),IDS_CONTAINS_CRITICALSYSOBJ,S_OK,
|
||
|
MB_ICONERROR, apv, 2);
|
||
|
}
|
||
|
|
||
|
Search.FreeColumn(&PathColumn);
|
||
|
Search.FreeColumn(&CriticalColumn);
|
||
|
bRet = false;
|
||
|
break;
|
||
|
}
|
||
|
Search.FreeColumn(&CriticalColumn);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// We found an NTDS Settings object. Report the error and return.
|
||
|
//
|
||
|
hr = Search.GetColumn(pAttrs[0], &PathColumn);
|
||
|
|
||
|
if (SUCCEEDED(hr) && PathColumn.dwNumValues == 1 && PathColumn.pADsValues != NULL)
|
||
|
{
|
||
|
CString strX500PathDC = PathColumn.pADsValues[PathColumn.dwNumValues-1].CaseIgnoreString;
|
||
|
Search.FreeColumn(&PathColumn);
|
||
|
if (lstrcmpi(lpszX500Path,strX500PathDC))
|
||
|
{
|
||
|
//
|
||
|
// retrieve the name of the DC
|
||
|
//
|
||
|
CComBSTR sbstrDCName;
|
||
|
bool fFoundNTDSDSA = FALSE;
|
||
|
ADS_SEARCH_COLUMN ClassColumn;
|
||
|
hr = Search.GetColumn (pAttrs[1], &ClassColumn);
|
||
|
if (SUCCEEDED(hr) && ClassColumn.dwNumValues > 0)
|
||
|
{
|
||
|
fFoundNTDSDSA = !lstrcmpi( L"nTDSDSA",
|
||
|
ClassColumn.pADsValues[ClassColumn.dwNumValues-1].CaseIgnoreString );
|
||
|
Search.FreeColumn(&ClassColumn);
|
||
|
pathCracker.Set((LPWSTR)(LPCWSTR)strX500PathDC, ADS_SETTYPE_DN);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
pathCracker.GetElement( (fFoundNTDSDSA)?1:0, &sbstrDCName );
|
||
|
}
|
||
|
|
||
|
|
||
|
// display an error message
|
||
|
PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPCTSTR)sbstrDCName};
|
||
|
(void) ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINS_DC,hr,
|
||
|
MB_ICONERROR, apv, 2);
|
||
|
|
||
|
// do not proceed with subtree deletion
|
||
|
bRet = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
hr = Search.GetNextRow();
|
||
|
}
|
||
|
|
||
|
// do not proceed with subtree deletion
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CDeleteDCDialog
|
||
|
|
||
|
class CDeleteDCDialog : public CDialog
|
||
|
{
|
||
|
// Construction
|
||
|
public:
|
||
|
CDeleteDCDialog(LPCTSTR lpszName, bool fIsComputer);
|
||
|
|
||
|
// Implementation
|
||
|
protected:
|
||
|
|
||
|
// message handlers and MFC overrides
|
||
|
virtual BOOL OnInitDialog();
|
||
|
virtual void OnOK();
|
||
|
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
|
||
|
private:
|
||
|
CString m_strADsPath;
|
||
|
bool m_fIsComputer;
|
||
|
};
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CDeleteDCDialog, CDialog)
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
CDeleteDCDialog::CDeleteDCDialog(LPCTSTR lpszADsPath, bool fIsComputer)
|
||
|
: CDialog(IDD_DELETE_DC_COMPUTER, NULL)
|
||
|
, m_strADsPath(lpszADsPath)
|
||
|
, m_fIsComputer(fIsComputer)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BOOL CDeleteDCDialog::OnInitDialog()
|
||
|
{
|
||
|
// CODEWORK AfxInit?
|
||
|
CDialog::OnInitDialog();
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set(const_cast<LPWSTR>((LPCWSTR)m_strADsPath), ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
|
||
|
|
||
|
CString strDisplay;
|
||
|
CComBSTR sbstrName;
|
||
|
if (m_fIsComputer)
|
||
|
{
|
||
|
pathCracker.GetElement( 0, &sbstrName );
|
||
|
strDisplay.FormatMessage(IDS_DELETE_DC_COMPUTERACCOUNT, sbstrName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CComBSTR sbstrSite;
|
||
|
pathCracker.GetElement( 1, &sbstrName );
|
||
|
pathCracker.GetElement( 3, &sbstrSite );
|
||
|
CString strTemp;
|
||
|
(void) GetDlgItemText(IDC_DELETE_DC_MAINTEXT, strTemp);
|
||
|
strDisplay.FormatMessage( strTemp, sbstrName, sbstrSite );
|
||
|
}
|
||
|
(void) SetDlgItemText( IDC_DELETE_DC_MAINTEXT, strDisplay );
|
||
|
|
||
|
CheckRadioButton( IDC_DELETE_DC_BADREASON1,
|
||
|
IDC_DELETE_DC_GOODREASON,
|
||
|
IDC_DELETE_DC_BADREASON1 );
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void CDeleteDCDialog::OnOK()
|
||
|
{
|
||
|
if (BST_CHECKED == IsDlgButtonChecked(IDC_DELETE_DC_GOODREASON))
|
||
|
{
|
||
|
CDialog::OnOK();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
(void) ReportErrorEx((HWND)*this,
|
||
|
(BST_CHECKED == IsDlgButtonChecked(IDC_DELETE_DC_BADREASON2))
|
||
|
? IDS_DELETE_DC_BADREASON2
|
||
|
: IDS_DELETE_DC_BADREASON1,
|
||
|
S_OK,MB_OK, NULL, 0);
|
||
|
|
||
|
CDialog::OnCancel();
|
||
|
}
|
||
|
|
||
|
|
||
|
// JonN 6/15/00 13574
|
||
|
// Centralizes the checks to make sure this is an OK object to delete
|
||
|
// returns HRESULT_FROM_WIN32(ERROR_CANCELLED) on cancellation
|
||
|
// returns refAlreadyDeleted=true iff ObjectDeletionCheck already
|
||
|
// attempted an alternate deletion method (e.g. DsRemoveDsServer).
|
||
|
HRESULT CUIOperationHandlerBase::ObjectDeletionCheck(
|
||
|
LPCTSTR lpszADsPath,
|
||
|
LPCTSTR lpszName, // shortname to display to user, may be NULL
|
||
|
LPCTSTR lpszClass,
|
||
|
bool& fAlternateDeleteMethod )
|
||
|
{
|
||
|
fAlternateDeleteMethod = false;
|
||
|
if (!_wcsicmp(L"user",lpszClass)
|
||
|
#ifdef INETORGPERSON
|
||
|
|| !_wcsicmp(L"inetOrgPerson", lpszClass)
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
if (IsThisUserLoggedIn(lpszADsPath))
|
||
|
{
|
||
|
CComBSTR sbstrRDN;
|
||
|
if (NULL == lpszName)
|
||
|
{
|
||
|
(void) DSPROP_RetrieveRDN( lpszADsPath, &sbstrRDN );
|
||
|
lpszName = sbstrRDN;
|
||
|
}
|
||
|
|
||
|
PVOID apv[1] = {(PVOID)lpszName};
|
||
|
if (IDYES != ReportMessageEx (GetParentHwnd(), IDS_12_USER_LOGGED_IN,
|
||
|
MB_YESNO, apv, 1))
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
bool fIsComputer = false;
|
||
|
if (!_wcsicmp(L"computer",lpszClass))
|
||
|
{
|
||
|
//
|
||
|
// Bind and figure out if the account is a DC
|
||
|
//
|
||
|
CComPtr<IADs> spIADs;
|
||
|
HRESULT hr = ADsOpenObject ((LPWSTR)lpszADsPath,
|
||
|
NULL, NULL, ADS_SECURE_AUTHENTICATION,
|
||
|
IID_IADs,
|
||
|
(void **)&spIADs);
|
||
|
CComVariant Var;
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = spIADs->Get(L"userAccountControl", &Var);
|
||
|
if ( FAILED(hr) || !(Var.lVal & UF_SERVER_TRUST_ACCOUNT))
|
||
|
return S_OK; // cannot be shown to be a DC
|
||
|
fIsComputer = true;
|
||
|
}
|
||
|
else if (!_wcsicmp(L"nTDSDSA",lpszClass))
|
||
|
{
|
||
|
//
|
||
|
// I would like to figure out the domain name so that I could
|
||
|
// use fCommit==FALSE, but this is a little complicated.
|
||
|
// Basic code is in proppage GetReplicatedDomainInfo(), but
|
||
|
// is not exportable in its current form. I will defer this
|
||
|
// improvement for later.
|
||
|
//
|
||
|
}
|
||
|
else if (!_wcsicmp(L"trustedDomain",lpszClass))
|
||
|
{
|
||
|
//
|
||
|
// Give a strong warning if they are trying to delete a
|
||
|
// TDO (Trusted Domain Object). This could cause serious
|
||
|
// problems but we want to allow them to clean up if necessary
|
||
|
//
|
||
|
PVOID apv[1] = {(PVOID)lpszName};
|
||
|
if (IDYES == ReportMessageEx( GetParentHwnd(),
|
||
|
IDS_WARNING_TDO_DELTEE,
|
||
|
MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING,
|
||
|
apv,
|
||
|
1 ) )
|
||
|
{
|
||
|
fAlternateDeleteMethod = FALSE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
}
|
||
|
}
|
||
|
else if (!_wcsicmp(L"interSiteTransport",lpszClass))
|
||
|
{
|
||
|
//
|
||
|
// 212232 JonN 10/27/00 Protect interSiteTransport objects
|
||
|
//
|
||
|
PVOID apv[1] = {(PVOID)lpszName};
|
||
|
(void) ReportMessageEx( GetParentHwnd(),
|
||
|
IDS_1_ERROR_DELETE_CRITOBJ,
|
||
|
MB_OK | MB_ICONERROR,
|
||
|
apv,
|
||
|
1 );
|
||
|
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return S_OK; // This is neither a computer nor an nTDSDSA nor a TDO
|
||
|
}
|
||
|
|
||
|
// This is either an nTDSDSA object, or a computer object
|
||
|
// which represents a DC
|
||
|
|
||
|
CDeleteDCDialog dlg(lpszADsPath,fIsComputer);
|
||
|
if (IDOK != dlg.DoModal())
|
||
|
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
|
||
|
if (fIsComputer)
|
||
|
return S_OK;
|
||
|
|
||
|
// This is an nTDSDSA. Delete using DsRemoveDsServer.
|
||
|
|
||
|
fAlternateDeleteMethod = true;
|
||
|
|
||
|
Smart_DsHandle shDS;
|
||
|
BOOL fLastDcInDomain = FALSE;
|
||
|
DWORD dwWinError = DsBind(
|
||
|
m_pComponentData->GetBasePathsInfo()->GetServerName(),
|
||
|
NULL,
|
||
|
&shDS );
|
||
|
if (ERROR_SUCCESS == dwWinError)
|
||
|
{
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set(const_cast<LPTSTR>(lpszADsPath), ADS_SETTYPE_FULL);
|
||
|
pathCracker.RemoveLeafElement(); // pass DN to Server object
|
||
|
CComBSTR sbstrDN;
|
||
|
pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrDN );
|
||
|
|
||
|
dwWinError = DsRemoveDsServer( shDS,
|
||
|
sbstrDN,
|
||
|
NULL,
|
||
|
&fLastDcInDomain,
|
||
|
TRUE );
|
||
|
}
|
||
|
|
||
|
return HRESULT_FROM_WIN32(dwWinError);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// CSingleDeleteHandlerBase
|
||
|
|
||
|
|
||
|
/*
|
||
|
NOTICE: the function will return S_OK on success, S_FALSE if aborted
|
||
|
by user, some FAILED(hr) otherwise
|
||
|
*/
|
||
|
HRESULT CSingleDeleteHandlerBase::Delete()
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
bool fAlternateDeleteMethod = false;
|
||
|
|
||
|
// start the transaction
|
||
|
hr = BeginTransaction();
|
||
|
ASSERT(SUCCEEDED(hr));
|
||
|
|
||
|
|
||
|
if (GetTransaction()->NeedNotifyCount() > 0)
|
||
|
{
|
||
|
CString szMessage, szAssocData;
|
||
|
szMessage.LoadString(IDS_CONFIRM_DELETE);
|
||
|
szAssocData.LoadString(IDS_EXTENS_SINGLE_DEL);
|
||
|
CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
|
||
|
dlg.SetStrings(szMessage, szAssocData);
|
||
|
if (IDNO == dlg.DoModal())
|
||
|
{
|
||
|
GetTransaction()->End();
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// this is just a message box, using ReportErrorEx for consistency of look
|
||
|
UINT answer = ReportErrorEx(GetParentHwnd(),IDS_CONFIRM_DELETE,S_OK,
|
||
|
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2, NULL, 0);
|
||
|
if (answer == IDNO || answer == IDCANCEL) {
|
||
|
return S_FALSE; // aborted by user
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CString szName;
|
||
|
GetItemName(szName);
|
||
|
|
||
|
hr = ObjectDeletionCheck(
|
||
|
GetItemPath(),
|
||
|
szName,
|
||
|
GetItemClass(),
|
||
|
fAlternateDeleteMethod );
|
||
|
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
|
||
|
return S_FALSE; // CODEWORK doesn't end transaction?
|
||
|
else if (FAILED(hr))
|
||
|
return hr; // CODEWORK doesn't end transaction?
|
||
|
|
||
|
// try to delete the object
|
||
|
if (!fAlternateDeleteMethod)
|
||
|
hr = DeleteObject();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// item deleted, notify extensions
|
||
|
GetTransaction()->Notify(0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// error in deleting item, check if it is a special error code
|
||
|
if (hr == HRESULT_FROM_WIN32(ERROR_DS_CANT_ON_NON_LEAF))
|
||
|
{
|
||
|
// ask user to if he/she wants to delete the whole subtree
|
||
|
PVOID apv[1] = {(LPWSTR)(LPCWSTR)szName};
|
||
|
UINT answer = ReportErrorEx(GetParentHwnd(),IDS_12_OBJECT_HAS_CHILDREN,hr,
|
||
|
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2, apv, 1);
|
||
|
if (answer == IDYES)
|
||
|
{
|
||
|
// JonN 5/22/00 Watch for potential NTDSDSA deletion
|
||
|
// JeffJon 8/10/00 Watch for potential critical object deletion (isCriticalSystemObject) bug #27377
|
||
|
//
|
||
|
if (CheckForCriticalSystemObjectInSubtree(GetItemPath(), szName))
|
||
|
{
|
||
|
//
|
||
|
// try to delete the subtree and continue trying if we reach the 16k limit
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
hr = DeleteSubtree();
|
||
|
} while (HRESULT_CODE(hr) == ERROR_DS_ADMIN_LIMIT_EXCEEDED);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
GetTransaction()->Notify(0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// failed subtree deletion, nothing can be done
|
||
|
PVOID apvToo[1] = {(LPWSTR)(LPCWSTR)szName};
|
||
|
ReportErrorEx(GetParentHwnd(), IDS_12_SUBTREE_DELETE_FAILED,hr,
|
||
|
MB_OK | MB_ICONERROR, apvToo, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (hr == E_ACCESSDENIED)
|
||
|
{
|
||
|
PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
|
||
|
ReportErrorEx(GetParentHwnd(), IDS_12_DELETE_ACCESS_DENIED, hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
}
|
||
|
else if (HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS)
|
||
|
{
|
||
|
PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
|
||
|
ReportErrorEx(GetParentHwnd(),IDS_12_DELETE_PRIMARY_GROUP_FAILED,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
|
||
|
ReportErrorEx(GetParentHwnd(),IDS_12_DELETE_FAILED,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
CStringList szDeletedPathList;
|
||
|
szDeletedPathList.AddTail(GetItemPath());
|
||
|
GetComponentData()->InvalidateSavedQueriesContainingObjects(szDeletedPathList);
|
||
|
}
|
||
|
return hr;
|
||
|
|
||
|
GetTransaction()->End();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// CMultipleDeleteHandlerBase
|
||
|
|
||
|
void CMultipleDeleteHandlerBase::Delete()
|
||
|
{
|
||
|
HRESULT hr = BeginTransaction();
|
||
|
ASSERT(SUCCEEDED(hr));
|
||
|
|
||
|
// ask confirmation to the user
|
||
|
UINT cCookieTotalCount = GetItemCount();
|
||
|
CString szFormat;
|
||
|
szFormat.LoadString(IDS_CONFIRM_MULTI_DELETE);
|
||
|
CString szMessage;
|
||
|
szMessage.Format((LPCWSTR)szFormat, cCookieTotalCount);
|
||
|
|
||
|
if (GetTransaction()->NeedNotifyCount() > 0)
|
||
|
{
|
||
|
CString szAssocData;
|
||
|
szAssocData.LoadString(IDS_EXTENS_MULTIPLE_DEL);
|
||
|
CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
|
||
|
dlg.SetStrings(szMessage, szAssocData);
|
||
|
if (IDNO == dlg.DoModal())
|
||
|
{
|
||
|
GetTransaction()->End();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
PVOID apv[1] = {(LPWSTR)(LPCWSTR)szMessage};
|
||
|
if (ReportErrorEx(GetParentHwnd(),IDS_STRING,S_OK,
|
||
|
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,
|
||
|
apv, 1)== IDNO)
|
||
|
{
|
||
|
return; // user aborted
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CMultipleDeleteProgressDialog dlg(GetParentHwnd(), GetComponentData(), this);
|
||
|
dlg.DoModal();
|
||
|
|
||
|
}
|
||
|
|
||
|
void CMultipleDeleteHandlerBase::OnStart(HWND hwnd)
|
||
|
{
|
||
|
SetParentHwnd(hwnd);
|
||
|
m_confirmationUI.SetWindow(GetParentHwnd());
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CMultipleDeleteHandlerBase::OnDeleteStep(IN UINT i,
|
||
|
OUT BOOL* pbContinue,
|
||
|
OUT CString& strrefPath,
|
||
|
OUT CString& strrefClass,
|
||
|
IN BOOL bSilent)
|
||
|
{
|
||
|
ASSERT(i < GetItemCount());
|
||
|
|
||
|
if (pbContinue == NULL)
|
||
|
{
|
||
|
return E_POINTER;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the OUT parameters
|
||
|
//
|
||
|
GetItemPath(i, strrefPath);
|
||
|
strrefClass = GetItemClass(i);
|
||
|
*pbContinue = TRUE;
|
||
|
|
||
|
//
|
||
|
// do the operation
|
||
|
//
|
||
|
HRESULT hr = DeleteObject(i);
|
||
|
if ((SUCCEEDED(hr)))
|
||
|
{
|
||
|
// item deleted, notify extensions and end transaction
|
||
|
GetTransaction()->Notify(i);
|
||
|
OnItemDeleted(i);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CString szName;
|
||
|
GetItemName(i, szName);
|
||
|
// error in deleting item, check if it is a special error code
|
||
|
if (hr == HRESULT_FROM_WIN32(ERROR_DS_CANT_ON_NON_LEAF))
|
||
|
{
|
||
|
// ask confirmation for deleting subtree
|
||
|
if (m_confirmationUI.CanDeleteSubtree(hr, szName, pbContinue))
|
||
|
{
|
||
|
// JeffJon 8/10/00 Watch for potential deletion of critical system objects
|
||
|
if ( !CheckForCriticalSystemObjectInSubtree(strrefPath, szName))
|
||
|
{
|
||
|
// error already reported
|
||
|
*pbContinue = FALSE;
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Delete the subtree and continue deleting if the 16k limit is reached
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
hr = DeleteSubtree(i);
|
||
|
} while (hr == ERROR_DS_ADMIN_LIMIT_EXCEEDED);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// item deleted, notify extensions and end transaction
|
||
|
GetTransaction()->Notify(i);
|
||
|
OnItemDeleted(i);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we failed subtree deletion
|
||
|
*pbContinue = m_confirmationUI.ErrorOnSubtreeDeletion(hr, szName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// This tells the calling function that we did not delete the object
|
||
|
// but don't add it to the error reporting
|
||
|
//
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we failed deletion
|
||
|
// JonN 7/20/00 If the HRESULT_FROM_WIN32(ERROR_CANCELLED) case,
|
||
|
// skip the confirmation UI and just cancel the series.
|
||
|
if (bSilent)
|
||
|
{
|
||
|
*pbContinue = hr != HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pbContinue = hr != HRESULT_FROM_WIN32(ERROR_CANCELLED) &&
|
||
|
m_confirmationUI.ErrorOnDeletion(hr, szName);
|
||
|
}
|
||
|
} // if (ERROR_DS_CANT_ON_NON_LEAF)
|
||
|
} // if (delete object)
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// CMoveHandlerBase
|
||
|
|
||
|
HRESULT CMoveHandlerBase::Move(LPCWSTR lpszDestinationPath)
|
||
|
{
|
||
|
// make sure destination data is reset
|
||
|
m_spDSDestination = NULL;
|
||
|
m_szDestPath.Empty();
|
||
|
m_szDestClass.Empty();
|
||
|
|
||
|
// check nomber of items
|
||
|
UINT nCount = GetItemCount();
|
||
|
if (nCount == 0)
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
// get the info about the destination container
|
||
|
HRESULT hr = _BrowseForDestination(lpszDestinationPath);
|
||
|
|
||
|
if (FAILED(hr) || (hr == S_FALSE))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First check to see if we are trying to move into the same container
|
||
|
// Using the path of the first object in the multiselect case is OK
|
||
|
//
|
||
|
CString szNewPath;
|
||
|
GetNewPath(0, szNewPath);
|
||
|
|
||
|
CPathCracker pathCracker;
|
||
|
hr = pathCracker.Set((PWSTR)(PCWSTR)szNewPath, ADS_SETTYPE_FULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pathCracker.RemoveLeafElement();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
CComBSTR sbstrContainerPath;
|
||
|
hr = pathCracker.Retrieve(ADS_FORMAT_X500, &sbstrContainerPath);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (0 == _wcsicmp(sbstrContainerPath, m_szDestPath))
|
||
|
{
|
||
|
//
|
||
|
// The source and the target container are the same so we
|
||
|
// don't have to do anything
|
||
|
//
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CStringList szMovedPathList;
|
||
|
for (UINT nIdx = 0; nIdx < GetItemCount(); nIdx++)
|
||
|
{
|
||
|
CString szPath;
|
||
|
GetItemPath(nIdx, szPath);
|
||
|
szMovedPathList.AddTail(szPath);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Notice that we fall through on a failure trying to crack the source parent path
|
||
|
// so reset the return value
|
||
|
//
|
||
|
hr = S_OK;
|
||
|
|
||
|
// do the actual move operation
|
||
|
if (nCount == 1)
|
||
|
{
|
||
|
BOOL bContinue = FALSE;
|
||
|
do
|
||
|
{
|
||
|
//
|
||
|
// Check to be sure we are not trying to drop on itself
|
||
|
//
|
||
|
if (m_szDestPath == szNewPath)
|
||
|
{
|
||
|
UINT nRet = ReportErrorEx(GetParentHwnd(), IDS_ERR_MSG_NO_MOVE_TO_SELF, S_OK,
|
||
|
MB_YESNO | MB_ICONERROR, NULL, 0);
|
||
|
|
||
|
if (nRet == IDYES)
|
||
|
{
|
||
|
// get the info about the destination container
|
||
|
hr = _BrowseForDestination(lpszDestinationPath);
|
||
|
|
||
|
if (FAILED(hr) || (hr == S_FALSE))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bContinue = TRUE;
|
||
|
}
|
||
|
} while (!bContinue);
|
||
|
|
||
|
if (bContinue)
|
||
|
{
|
||
|
hr = _MoveSingleSel(szNewPath);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
GetComponentData()->InvalidateSavedQueriesContainingObjects(szMovedPathList);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
hr = _MoveMultipleSel();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CMoveHandlerBase::_ReportFailure(BOOL bLast, HRESULT hr, LPCWSTR lpszName)
|
||
|
{
|
||
|
TRACE(_T("Object Move Failed with hr: %lx\n"), hr);
|
||
|
PVOID apv[1] = {(LPWSTR)lpszName};
|
||
|
if (bLast)
|
||
|
{
|
||
|
// single selection or last one in multi selection
|
||
|
ReportErrorEx(GetParentHwnd(),IDS_12_FAILED_TO_MOVE_OBJECT,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
return FALSE; // do not continue
|
||
|
}
|
||
|
return (ReportErrorEx(GetParentHwnd(),IDS_12_MULTI_FAILED_TO_MOVE_OBJECT,hr,
|
||
|
MB_YESNO, apv, 1) == IDYES);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CMoveHandlerBase::_MoveSingleSel(PCWSTR pszNewPath)
|
||
|
{
|
||
|
if (!_BeginTransactionAndConfirmOperation())
|
||
|
return S_FALSE;
|
||
|
|
||
|
CComPtr<IDispatch> spDSTargetIDispatch;
|
||
|
HRESULT hr = m_spDSDestination->MoveHere(const_cast<PWSTR>(pszNewPath),
|
||
|
NULL,
|
||
|
&spDSTargetIDispatch);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
CString szName;
|
||
|
GetName(0, szName);
|
||
|
_ReportFailure(TRUE, hr, szName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// all went fine, notify extensions
|
||
|
GetTransaction()->Notify(0);
|
||
|
|
||
|
// give a chance to update UI (e.g. cookies)
|
||
|
CComPtr<IADs> spIADsTarget;
|
||
|
hr = spDSTargetIDispatch->QueryInterface (IID_IADs,
|
||
|
(void **)&spIADsTarget);
|
||
|
|
||
|
hr = OnItemMoved(0, spIADsTarget);
|
||
|
}
|
||
|
GetTransaction()->End();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CMoveHandlerBase::_MoveMultipleSel()
|
||
|
{
|
||
|
if (!_BeginTransactionAndConfirmOperation())
|
||
|
return S_FALSE;
|
||
|
|
||
|
CMultipleMoveProgressDialog dlg(GetParentHwnd(), GetComponentData(), this);
|
||
|
dlg.DoModal();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CMoveHandlerBase::_OnMoveStep(IN UINT i,
|
||
|
OUT BOOL* pbCanContinue,
|
||
|
OUT CString& strrefPath,
|
||
|
OUT CString& strrefClass)
|
||
|
{
|
||
|
ASSERT(m_spDSDestination != NULL);
|
||
|
|
||
|
if (pbCanContinue == NULL)
|
||
|
{
|
||
|
return E_POINTER;
|
||
|
}
|
||
|
|
||
|
UINT nCount = GetItemCount();
|
||
|
|
||
|
//
|
||
|
// Initialize out parameters
|
||
|
//
|
||
|
GetItemPath(i, strrefPath);
|
||
|
strrefClass = GetItemClass(i);
|
||
|
*pbCanContinue = TRUE;
|
||
|
|
||
|
if (strrefPath == m_szDestPath)
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
// try to execute the move
|
||
|
CString szNewPath;
|
||
|
|
||
|
CComPtr<IDispatch> spDSTargetIDispatch;
|
||
|
GetNewPath(i, szNewPath);
|
||
|
HRESULT hr = m_spDSDestination->MoveHere((LPWSTR)(LPCWSTR)szNewPath,
|
||
|
NULL,
|
||
|
&spDSTargetIDispatch);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
CString szName;
|
||
|
GetName(i, szName);
|
||
|
if (nCount == 1)
|
||
|
{
|
||
|
*pbCanContinue = _ReportFailure((i == nCount-1), hr, szName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// all went fine, notify extensions
|
||
|
GetTransaction()->Notify(i);
|
||
|
|
||
|
// give a chance to update UI (e.g. cookies)
|
||
|
CComPtr<IADs> spIADsTarget;
|
||
|
hr = spDSTargetIDispatch->QueryInterface (IID_IADs,
|
||
|
(void **)&spIADsTarget);
|
||
|
|
||
|
hr = OnItemMoved(i, spIADsTarget);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void BuildBrowseQueryString(LPCWSTR lpszSchemaNamingContext, BOOL bAdvancedView,
|
||
|
CString& szQueryString)
|
||
|
{
|
||
|
// allowed list of container classes
|
||
|
static LPCWSTR lpszAllowedContainers[] =
|
||
|
{
|
||
|
L"Organizational-Unit",
|
||
|
L"Builtin-Domain",
|
||
|
L"Lost-And-Found",
|
||
|
L"container",
|
||
|
NULL // end of table
|
||
|
};
|
||
|
|
||
|
CString sz = L"(|";
|
||
|
for (int k=0; lpszAllowedContainers[k] != NULL; k++)
|
||
|
{
|
||
|
sz += L"(ObjectCategory=CN=";
|
||
|
sz += lpszAllowedContainers[k];
|
||
|
sz += L",";
|
||
|
sz += lpszSchemaNamingContext;
|
||
|
sz += L")";
|
||
|
}
|
||
|
sz += L")";
|
||
|
|
||
|
if( bAdvancedView )
|
||
|
{
|
||
|
szQueryString = sz;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
szQueryString.Format(L"(&%s(!showInAdvancedViewOnly=TRUE))", (LPCWSTR)sz);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int BrowseCallback(HWND, UINT uMsg, LPARAM lParam, LPARAM lpData)
|
||
|
{
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case DSBM_HELP:
|
||
|
{
|
||
|
TRACE(L"Browse Callback: msg is DSBM_HELP.\n");
|
||
|
LPHELPINFO pHelp = (LPHELPINFO) lParam;
|
||
|
TRACE(_T("CtrlId = %d, ContextId = 0x%x\n"),
|
||
|
pHelp->iCtrlId, pHelp->dwContextId);
|
||
|
if (pHelp->iCtrlId != DSBID_CONTAINERLIST) {
|
||
|
return 0; // not handled
|
||
|
}
|
||
|
::WinHelp((HWND)pHelp->hItemHandle,
|
||
|
DSADMIN_CONTEXT_HELP_FILE,
|
||
|
HELP_WM_HELP,
|
||
|
(DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_BROWSE_CONTAINER);
|
||
|
}
|
||
|
break;
|
||
|
case DSBM_GETBROWSEDATA:
|
||
|
{
|
||
|
// called to change the LDAP query string
|
||
|
TRACE(L"Browse Callback: msg is DSBM_HELP.\n");
|
||
|
CDSComponentData* pCD = (CDSComponentData*)lpData;
|
||
|
|
||
|
if ( (pCD == NULL) || (SNAPINTYPE_SITE == pCD->QuerySnapinType()) ||
|
||
|
pCD->ExpandComputers() )
|
||
|
{
|
||
|
// if we are in site and repl we do not change the default
|
||
|
// string because we do not have too many objects
|
||
|
|
||
|
// if we expand computer, users, etc, we do not really
|
||
|
// have a choice but allow (objectClass=*)
|
||
|
TRACE(L"Browse Callback: DSBM_HELP, leave the default setting.\n");
|
||
|
|
||
|
return 0; // just leave the default setting
|
||
|
}
|
||
|
|
||
|
DSBROWSEDATA* pdbd = (DSBROWSEDATA*)lParam;
|
||
|
|
||
|
TRACE(L"pdbd->pszFilter = %s\n", pdbd->pszFilter);
|
||
|
TRACE(L"pdbd->cchFilter = %d\n", pdbd->cchFilter);
|
||
|
|
||
|
TRACE(L"pdbd->pszNameAttribute = %s\n", pdbd->pszNameAttribute);
|
||
|
TRACE(L"pdbd->cchNameAttribute = %d\n", pdbd->cchNameAttribute);
|
||
|
|
||
|
// need to change query string
|
||
|
CString szQueryString;
|
||
|
FilterElementStruct* pFilterElementStructDrillDown = &g_filterelementDsAdminHardcoded;
|
||
|
BuildFilterElementString(szQueryString, pFilterElementStructDrillDown,
|
||
|
pCD->GetBasePathsInfo()->GetSchemaNamingContext());
|
||
|
|
||
|
szQueryString = L"(|" + szQueryString + L")";
|
||
|
/*
|
||
|
BuildBrowseQueryString(pCD->GetBasePathsInfo()->GetSchemaNamingContext(),
|
||
|
pCD->IsAdvancedView(), szQueryString);
|
||
|
*/
|
||
|
// copy over the new filter
|
||
|
int nNewFilterLen = szQueryString.GetLength();
|
||
|
ASSERT(nNewFilterLen < pdbd->cchFilter);
|
||
|
if (nNewFilterLen >= pdbd->cchFilter)
|
||
|
{
|
||
|
return 0; // failed, filter string too long
|
||
|
}
|
||
|
|
||
|
wcscpy(pdbd->pszFilter, szQueryString);
|
||
|
pdbd->cchFilter = nNewFilterLen;
|
||
|
TRACE(L"New pdbd->pszFilter = %s\n", pdbd->pszFilter);
|
||
|
TRACE(L"New pdbd->cchFilter = %d\n", pdbd->cchFilter);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
} // switch
|
||
|
|
||
|
return 1; // handled
|
||
|
}
|
||
|
|
||
|
HRESULT CMoveHandlerBase::_BrowseForDestination(LPCWSTR lpszDestinationPath)
|
||
|
|
||
|
{
|
||
|
m_spDSDestination = NULL;
|
||
|
m_szDestPath.Empty();
|
||
|
m_szDestClass.Empty();
|
||
|
|
||
|
// check if we have to expand computers in the Browse for container UI
|
||
|
CDSComponentData* pCD = GetComponentData();
|
||
|
BOOL bExpandComputers = FALSE;
|
||
|
if (pCD != NULL)
|
||
|
{
|
||
|
bExpandComputers = pCD->ExpandComputers();
|
||
|
}
|
||
|
|
||
|
// determine if we have to show the Browse for container dialog
|
||
|
CString strTargetContainer;
|
||
|
|
||
|
if (lpszDestinationPath != NULL)
|
||
|
{
|
||
|
// we have the target container already, no need to
|
||
|
// bring up UI
|
||
|
strTargetContainer = lpszDestinationPath;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no container, need Browse dialog
|
||
|
CString strClassOfMovedItem;
|
||
|
GetClassOfMovedItem(strClassOfMovedItem);
|
||
|
if (0 == strClassOfMovedItem.CompareNoCase(L"server"))
|
||
|
{
|
||
|
HICON hIcon = NULL;
|
||
|
if (pCD != NULL)
|
||
|
{
|
||
|
MyBasePathsInfo* pBasePathsInfo = pCD->GetBasePathsInfo();
|
||
|
ASSERT(pBasePathsInfo != NULL);
|
||
|
hIcon = pBasePathsInfo->GetIcon(const_cast<LPTSTR>(gsz_site),
|
||
|
DSGIF_ISNORMAL | DSGIF_GETDEFAULTICON | DSGIF_DEFAULTISCONTAINER,
|
||
|
16, 16);
|
||
|
}
|
||
|
CMoveServerDialog dlg( m_lpszBrowseRootPath, hIcon, CWnd::FromHandle(GetParentHwnd()) );
|
||
|
INT_PTR result = dlg.DoModal();
|
||
|
if (IDCANCEL == result)
|
||
|
return S_FALSE;
|
||
|
strTargetContainer = dlg.m_strTargetContainer;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PWSTR pszPath = new WCHAR[INTERNET_MAX_URL_LENGTH * 4];
|
||
|
if (!pszPath)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
pszPath[0] = TEXT('\0');
|
||
|
|
||
|
CString strTitle;
|
||
|
strTitle.LoadString (IDS_MOVE_TITLE);
|
||
|
|
||
|
DSBROWSEINFO dsbi;
|
||
|
::ZeroMemory( &dsbi, sizeof(dsbi) );
|
||
|
|
||
|
CString str;
|
||
|
str.LoadString(IDS_MOVE_TARGET);
|
||
|
|
||
|
dsbi.hwndOwner = GetParentHwnd();
|
||
|
// CODEWORK: Get DsBrowseForContainer to take const strings
|
||
|
dsbi.cbStruct = sizeof (DSBROWSEINFO);
|
||
|
dsbi.pszCaption = (LPWSTR)((LPCWSTR)strTitle); // this is actually the caption
|
||
|
dsbi.pszTitle = (LPWSTR)((LPCWSTR)str);
|
||
|
dsbi.pszRoot = (LPWSTR)m_lpszBrowseRootPath;
|
||
|
dsbi.pszPath = pszPath;
|
||
|
dsbi.cchPath = INTERNET_MAX_URL_LENGTH * 4;
|
||
|
dsbi.dwFlags = DSBI_RETURN_FORMAT |
|
||
|
DSBI_EXPANDONOPEN;
|
||
|
if( pCD && pCD->IsAdvancedView() )
|
||
|
{
|
||
|
dsbi.dwFlags |= DSBI_INCLUDEHIDDEN;
|
||
|
}
|
||
|
if (bExpandComputers)
|
||
|
{
|
||
|
dsbi.dwFlags |= DSBI_IGNORETREATASLEAF;
|
||
|
}
|
||
|
dsbi.pfnCallback = BrowseCallback;
|
||
|
dsbi.lParam = (LPARAM)pCD;
|
||
|
dsbi.dwReturnFormat = ADS_FORMAT_X500;
|
||
|
|
||
|
DWORD result = DsBrowseForContainer( &dsbi ); // returns -1, 0, IDOK or IDCANCEL
|
||
|
if (result != IDOK)
|
||
|
{
|
||
|
if (pszPath)
|
||
|
{
|
||
|
delete[] pszPath;
|
||
|
pszPath = 0;
|
||
|
}
|
||
|
return S_FALSE; // canceled by user
|
||
|
}
|
||
|
strTargetContainer = dsbi.pszPath;
|
||
|
|
||
|
if (pszPath)
|
||
|
{
|
||
|
delete[] pszPath;
|
||
|
pszPath = 0;
|
||
|
}
|
||
|
} // class is not server
|
||
|
} // have target container
|
||
|
|
||
|
if ( strTargetContainer.IsEmpty() ) // ADSI doesn't like this
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
// try to open the target container
|
||
|
CComPtr<IADsContainer> spDSDestination;
|
||
|
HRESULT hr = DSAdminOpenObject(strTargetContainer,
|
||
|
IID_IADsContainer,
|
||
|
(void **)&spDSDestination,
|
||
|
FALSE /*bServer*/);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
CPathCracker pathCracker;
|
||
|
pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strTargetContainer),
|
||
|
ADS_SETTYPE_FULL);
|
||
|
pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
||
|
CComBSTR DestName;
|
||
|
pathCracker.GetElement( 0, &DestName );
|
||
|
PVOID apv[1] = {(BSTR)DestName};
|
||
|
ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINER_NOT_FOUND,hr,
|
||
|
MB_OK | MB_ICONERROR, apv, 1);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// need the class of the destination container
|
||
|
CComPtr<IADs> spIADs;
|
||
|
hr = spDSDestination->QueryInterface(IID_IADs, (void**)&spIADs);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
CComBSTR bstrClass;
|
||
|
hr = spIADs->get_Class(&bstrClass);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// all went well, copy the output parameters
|
||
|
m_spDSDestination = spDSDestination;
|
||
|
m_szDestPath = strTargetContainer;
|
||
|
m_szDestClass = bstrClass;
|
||
|
m_bDestContainer = TRUE; // we do a move, it must be one
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
BOOL CMoveHandlerBase::_BeginTransactionAndConfirmOperation()
|
||
|
{
|
||
|
// start the transaction
|
||
|
HRESULT hr = BeginTransaction();
|
||
|
ASSERT(SUCCEEDED(hr));
|
||
|
// if needed, confirm
|
||
|
if (GetTransaction()->NeedNotifyCount() > 0)
|
||
|
{
|
||
|
CString szMessage;
|
||
|
CString szAssocData;
|
||
|
UINT nCount = GetItemCount();
|
||
|
if (nCount == 1)
|
||
|
{
|
||
|
szMessage.LoadString(IDS_CONFIRM_MOVE);
|
||
|
szAssocData.LoadString(IDS_EXTENS_SINGLE_MOVE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CString szMessageFormat;
|
||
|
szMessageFormat.LoadString(IDS_CONFIRM_MULTIPLE_MOVE);
|
||
|
szMessage.Format(szMessageFormat, nCount);
|
||
|
szAssocData.LoadString(IDS_EXTENS_MULTIPLE_MOVE);
|
||
|
}
|
||
|
CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
|
||
|
dlg.SetStrings(szMessage, szAssocData);
|
||
|
|
||
|
if (IDNO == dlg.DoModal())
|
||
|
{
|
||
|
GetTransaction()->End();
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////
|
||
|
// IsHomogenousDSSelection
|
||
|
//
|
||
|
// pDataObject must support the DSAdmin internal clipboard format
|
||
|
//
|
||
|
// if the return value is true, szClassName will be the name of
|
||
|
// the class of the homogenous selection
|
||
|
//
|
||
|
BOOL IsHomogenousDSSelection(LPDATAOBJECT pDataObject, CString& szClassName)
|
||
|
{
|
||
|
BOOL bHomogenous = TRUE;
|
||
|
szClassName = L"";
|
||
|
|
||
|
if (pDataObject == NULL)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CInternalFormatCracker ifc;
|
||
|
HRESULT hr = ifc.Extract(pDataObject);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CUINode* pUINode = ifc.GetCookie();
|
||
|
ASSERT(pUINode != NULL);
|
||
|
|
||
|
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(pUINode);
|
||
|
if (pDSUINode == NULL)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CDSCookie* pCookie = GetDSCookieFromUINode(pDSUINode);
|
||
|
if (pCookie == NULL)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
szClassName = pCookie->GetClass();
|
||
|
ASSERT(!szClassName.IsEmpty());
|
||
|
|
||
|
for (UINT idx = 1; idx < ifc.GetCookieCount(); idx++)
|
||
|
{
|
||
|
CUINode* pSelectedUINode = ifc.GetCookie(idx);
|
||
|
ASSERT(pSelectedUINode);
|
||
|
|
||
|
CDSUINode* pSelectedDSUINode = dynamic_cast<CDSUINode*>(pSelectedUINode);
|
||
|
if (!pSelectedDSUINode)
|
||
|
{
|
||
|
bHomogenous = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
CDSCookie* pSelectedCookie = GetDSCookieFromUINode(pSelectedDSUINode);
|
||
|
if (!pSelectedCookie)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
bHomogenous = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (wcscmp(szClassName, pSelectedCookie->GetClass()) != 0)
|
||
|
{
|
||
|
bHomogenous = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!bHomogenous)
|
||
|
{
|
||
|
szClassName = L"";
|
||
|
}
|
||
|
return bHomogenous;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////////
|
||
|
// Temporary Tab Collector stuff
|
||
|
//
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: AddPageProc
|
||
|
//
|
||
|
// Synopsis: The IShellPropSheetExt->AddPages callback.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BOOL CALLBACK AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall)
|
||
|
{
|
||
|
TRACE(_T("xx.%03x> AddPageProc()\n"), GetCurrentThreadId());
|
||
|
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = ((LPPROPERTYSHEETCALLBACK)pCall)->AddPage(hPage);
|
||
|
|
||
|
return hr == S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT GetDisplaySpecifierProperty(PCWSTR pszClassName,
|
||
|
PCWSTR pszDisplayProperty,
|
||
|
MyBasePathsInfo* pBasePathsInfo,
|
||
|
CStringList& strListRef,
|
||
|
bool bEnglishOnly)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//
|
||
|
// Validate parameters
|
||
|
// Note : pszClassName can be NULL and will retrieve the default-Display values
|
||
|
//
|
||
|
if (pszDisplayProperty == NULL ||
|
||
|
pBasePathsInfo == NULL)
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return E_POINTER;
|
||
|
}
|
||
|
|
||
|
CComPtr<IADs> spIADs;
|
||
|
if (!bEnglishOnly)
|
||
|
{
|
||
|
hr = pBasePathsInfo->GetDisplaySpecifier(pszClassName, IID_IADs, (PVOID*)&spIADs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Build the path to the English display specifier container
|
||
|
//
|
||
|
CString szConfigDN = pBasePathsInfo->GetConfigNamingContext();
|
||
|
CString szEnglishDisplaySpecifierDN = L"CN=409,CN=DisplaySpecifiers," + szConfigDN;
|
||
|
CString szDisplayObjectDN = L"CN=" + CString(pszClassName) + L"-Display," + szEnglishDisplaySpecifierDN;
|
||
|
|
||
|
CString szDisplayObjectPath;
|
||
|
pBasePathsInfo->ComposeADsIPath(szDisplayObjectPath, szDisplayObjectDN);
|
||
|
|
||
|
//
|
||
|
// Open the object and get the property
|
||
|
//
|
||
|
hr = DSAdminOpenObject(szDisplayObjectPath,
|
||
|
IID_IADs,
|
||
|
(void**)&spIADs,
|
||
|
true);
|
||
|
}
|
||
|
if (SUCCEEDED(hr) && !!spIADs)
|
||
|
{
|
||
|
CComVariant var;
|
||
|
hr = spIADs->Get((LPWSTR)pszDisplayProperty, &var);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = HrVariantToStringList(var, strListRef);
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT TabCollect_GetDisplayGUIDs(LPCWSTR lpszClassName,
|
||
|
LPCWSTR lpszDisplayProperty,
|
||
|
MyBasePathsInfo* pBasePathsInfo,
|
||
|
UINT* pnCount,
|
||
|
GUID** ppGuids)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//
|
||
|
// This should bind to the display specifiers, get the specified property and
|
||
|
// sort the guids by ordered pairs and return the guids
|
||
|
//
|
||
|
if (pBasePathsInfo == NULL)
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
CStringList szPropertyList;
|
||
|
|
||
|
hr = GetDisplaySpecifierProperty(lpszClassName, lpszDisplayProperty, pBasePathsInfo, szPropertyList);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
if (szPropertyList.GetCount() < 1)
|
||
|
{
|
||
|
//
|
||
|
// Couldn't find anything for the class, try to find something in the default-Display
|
||
|
//
|
||
|
hr = GetDisplaySpecifierProperty(L"default", lpszDisplayProperty, pBasePathsInfo, szPropertyList);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
//
|
||
|
// If still nothing is found revert to the English display specifiers
|
||
|
//
|
||
|
hr = GetDisplaySpecifierProperty(lpszClassName, lpszDisplayProperty, pBasePathsInfo, szPropertyList, true);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
if (szPropertyList.GetCount() < 1)
|
||
|
{
|
||
|
//
|
||
|
// Now try the English default
|
||
|
//
|
||
|
hr = GetDisplaySpecifierProperty(L"default", lpszDisplayProperty, pBasePathsInfo, szPropertyList, true);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pnCount = static_cast<UINT>(szPropertyList.GetCount());
|
||
|
*ppGuids = new GUID[*pnCount];
|
||
|
if (*ppGuids == NULL)
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
int* pnIndex = new int[*pnCount];
|
||
|
if (pnIndex == NULL)
|
||
|
{
|
||
|
*pnCount = 0;
|
||
|
*ppGuids = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CString szIndex;
|
||
|
CString szGUID;
|
||
|
UINT itr = 0;
|
||
|
|
||
|
POSITION pos = szPropertyList.GetHeadPosition();
|
||
|
while (pos != NULL)
|
||
|
{
|
||
|
CString szItem = szPropertyList.GetNext(pos);
|
||
|
|
||
|
int nComma = szItem.Find(L",");
|
||
|
if (nComma == -1)
|
||
|
continue;
|
||
|
|
||
|
szIndex = szItem.Left(nComma);
|
||
|
int nIndex = _wtoi((LPCWSTR)szIndex);
|
||
|
if (nIndex <= 0)
|
||
|
continue; // allow from 1 up
|
||
|
|
||
|
// strip leading and traling blanks
|
||
|
szGUID = szItem.Mid(nComma+1);
|
||
|
szGUID.TrimLeft();
|
||
|
szGUID.TrimRight();
|
||
|
|
||
|
GUID guid;
|
||
|
hr = ::CLSIDFromString((LPWSTR)(LPCWSTR)szGUID, &guid);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
(*ppGuids)[itr] = guid;
|
||
|
pnIndex[itr] = nIndex;
|
||
|
itr++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Must sort the page list
|
||
|
//
|
||
|
while (TRUE)
|
||
|
{
|
||
|
BOOL bSwapped = FALSE;
|
||
|
for (UINT k=1; k < *pnCount; k++)
|
||
|
{
|
||
|
if (pnIndex[k] < pnIndex[k-1])
|
||
|
{
|
||
|
// swap
|
||
|
int nTemp = pnIndex[k];
|
||
|
pnIndex[k] = pnIndex[k-1];
|
||
|
pnIndex[k-1] = nTemp;
|
||
|
GUID temp = *ppGuids[k];
|
||
|
*ppGuids[k] = *ppGuids[k-1];
|
||
|
*ppGuids[k-1] = temp;
|
||
|
bSwapped = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!bSwapped)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cleanup the index array
|
||
|
//
|
||
|
if (pnIndex != NULL)
|
||
|
{
|
||
|
delete[] pnIndex;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//**********************************************************************
|
||
|
|
||
|
// Test code to improve the search process on cookies
|
||
|
|
||
|
BOOL _SearchTreeForCookie(IN CUINode* pContainerNode, // current container where to start the search
|
||
|
IN CPathCracker* pPathCracker, // path cracker with the tokenized search path
|
||
|
IN long nCurrentToken, // current token in the path cracker
|
||
|
IN BOOL bSearchSubcontainers, // flag to search subcontainers
|
||
|
OUT CUINode** ppUINode // returned node
|
||
|
)
|
||
|
{
|
||
|
ASSERT(pContainerNode != NULL);
|
||
|
ASSERT(pContainerNode->IsContainer());
|
||
|
|
||
|
long nPathElements = 0;
|
||
|
pPathCracker->GetNumElements(&nPathElements);
|
||
|
|
||
|
if (nCurrentToken >= nPathElements)
|
||
|
{
|
||
|
// ran out of tokens to compare
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
CComBSTR bstrCurrentToken;
|
||
|
pPathCracker->GetElement(nCurrentToken, &bstrCurrentToken);
|
||
|
|
||
|
|
||
|
// decide which list to look into
|
||
|
CUINodeList* pNodeList = NULL;
|
||
|
if (bSearchSubcontainers)
|
||
|
pNodeList = pContainerNode->GetFolderInfo()->GetContainerList();
|
||
|
else
|
||
|
pNodeList = pContainerNode->GetFolderInfo()->GetLeafList();
|
||
|
|
||
|
|
||
|
CPathCracker pathCrackerCurr;
|
||
|
|
||
|
for (POSITION pos = pNodeList->GetHeadPosition(); pos != NULL; )
|
||
|
{
|
||
|
CUINode* pCurrentNode = pNodeList->GetNext(pos);
|
||
|
if (!IS_CLASS(*pCurrentNode, CDSUINode))
|
||
|
{
|
||
|
// not a node with a cookie, just skip
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// get the cookie from the node
|
||
|
CDSCookie* pCurrentCookie = GetDSCookieFromUINode(pCurrentNode);
|
||
|
|
||
|
// build the naming token (leaf element of the path), e.g. "CN=foo"
|
||
|
CComBSTR bstrCurrentNamingToken;
|
||
|
pathCrackerCurr.Set((BSTR)pCurrentCookie->GetPath(), ADS_SETTYPE_DN);
|
||
|
pathCrackerCurr.GetElement(0, &bstrCurrentNamingToken);
|
||
|
|
||
|
|
||
|
// compare the current naming token with the current search token
|
||
|
TRACE(L"comparing bstrCurrentToken = %s, bstrCurrentNamingToken = %s\n",
|
||
|
bstrCurrentToken, bstrCurrentNamingToken);
|
||
|
|
||
|
if (_wcsicmp(bstrCurrentToken, bstrCurrentNamingToken) == 0)
|
||
|
{
|
||
|
// the token matches, need to see if we are at the end of the
|
||
|
// list of tokens
|
||
|
if (nCurrentToken == 0)
|
||
|
{
|
||
|
*ppUINode = pCurrentNode;
|
||
|
return TRUE; // got it!!!
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we match, but we have to go one level deeper
|
||
|
BOOL bFound = FALSE;
|
||
|
if (nCurrentToken == 1)
|
||
|
{
|
||
|
// try on leaf nodes, we are at the last level
|
||
|
bFound = _SearchTreeForCookie(pCurrentNode, pPathCracker, nCurrentToken-1, FALSE, ppUINode);
|
||
|
}
|
||
|
|
||
|
if (bFound)
|
||
|
return TRUE;
|
||
|
|
||
|
// try on subcontainers
|
||
|
return _SearchTreeForCookie(pCurrentNode, pPathCracker, nCurrentToken-1, TRUE, ppUINode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if no match, we keep scanning at this level
|
||
|
} // for
|
||
|
|
||
|
return FALSE; // not found
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FindCookieInSubtree(IN CUINode* pContainerNode,
|
||
|
IN LPCWSTR lpszCookieDN,
|
||
|
IN SnapinType snapinType,
|
||
|
OUT CUINode** ppUINode)
|
||
|
{
|
||
|
*ppUINode = NULL;
|
||
|
|
||
|
|
||
|
if (!pContainerNode->IsContainer())
|
||
|
{
|
||
|
// not the right type of node
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPCWSTR lpszStartingContainerPath = NULL;
|
||
|
long nAdjustLevel = 0;
|
||
|
if (IS_CLASS(*pContainerNode, CDSUINode) )
|
||
|
{
|
||
|
lpszStartingContainerPath = dynamic_cast<CDSUINode*>(pContainerNode)->GetCookie()->GetPath();
|
||
|
nAdjustLevel = 1;
|
||
|
}
|
||
|
else if (IS_CLASS(*pContainerNode, CRootNode) )
|
||
|
{
|
||
|
lpszStartingContainerPath = dynamic_cast<CRootNode*>(pContainerNode)->GetPath();
|
||
|
if (snapinType == SNAPINTYPE_SITE)
|
||
|
{
|
||
|
nAdjustLevel = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lpszStartingContainerPath == NULL)
|
||
|
{
|
||
|
// bad node type
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// instantiate a path cracker for the DN we are in search of
|
||
|
CPathCracker pathCrackerDN;
|
||
|
HRESULT hr = pathCrackerDN.Set((BSTR)lpszCookieDN, ADS_SETTYPE_DN);
|
||
|
|
||
|
long nPathElementsDN = 0;
|
||
|
hr = pathCrackerDN.GetNumElements(&nPathElementsDN);
|
||
|
|
||
|
if ( FAILED(hr) || (nPathElementsDN <= 0) )
|
||
|
{
|
||
|
// bad path
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// instantiate a path cracker for the container node
|
||
|
CPathCracker pathCrackerStartingContainer;
|
||
|
pathCrackerStartingContainer.Set((BSTR)lpszStartingContainerPath, ADS_SETTYPE_DN);
|
||
|
long nPathElementsStartingContainer = 0;
|
||
|
pathCrackerStartingContainer.GetNumElements(&nPathElementsStartingContainer);
|
||
|
|
||
|
if ( FAILED(hr) || (nPathElementsStartingContainer <= 0) )
|
||
|
{
|
||
|
// bad path
|
||
|
ASSERT(FALSE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// compute the level where we start the search from
|
||
|
long nStartToken = nPathElementsDN - nPathElementsStartingContainer - nAdjustLevel;
|
||
|
if ( nStartToken < 0)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (( nStartToken == 0) && (nAdjustLevel == 1) && snapinType != SNAPINTYPE_SITE)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return _SearchTreeForCookie(pContainerNode, &pathCrackerDN, nStartToken /*current token*/, TRUE, ppUINode);
|
||
|
}
|
||
|
|
||
|
|
||
|
//**********************************************************************
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// CMultiselectMoveDataObject
|
||
|
|
||
|
|
||
|
// helper function for CDSEvent::_Paste()
|
||
|
// to create a data object containing the successfully pasted items
|
||
|
HRESULT CMultiselectMoveDataObject::BuildPastedDataObject(
|
||
|
IN CObjectNamesFormatCracker* pObjectNamesFormatPaste,
|
||
|
IN CMultiselectMoveHandler* pMoveHandler,
|
||
|
IN CDSComponentData* pCD,
|
||
|
OUT IDataObject** ppSuccesfullyPastedDataObject)
|
||
|
{
|
||
|
// verify input parameters
|
||
|
if (ppSuccesfullyPastedDataObject == NULL)
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
*ppSuccesfullyPastedDataObject = NULL;
|
||
|
|
||
|
if ((pObjectNamesFormatPaste == NULL) || (pMoveHandler == NULL) )
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// need to build a data object and hang on to it
|
||
|
//
|
||
|
CComObject<CMultiselectMoveDataObject>* pObject;
|
||
|
|
||
|
CComObject<CMultiselectMoveDataObject>::CreateInstance(&pObject);
|
||
|
if (pObject == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT hr = pObject->FinalConstruct();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pObject;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
hr = pObject->Init(pObjectNamesFormatPaste, pMoveHandler, pCD);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pObject;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
hr = pObject->QueryInterface(IID_IDataObject,
|
||
|
reinterpret_cast<void**>(ppSuccesfullyPastedDataObject));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
//
|
||
|
// delete object by calling Release()
|
||
|
//
|
||
|
(*ppSuccesfullyPastedDataObject)->Release();
|
||
|
(*ppSuccesfullyPastedDataObject) = NULL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
CLIPFORMAT CMultiselectMoveDataObject::m_cfDsObjectNames =
|
||
|
(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
|
||
|
|
||
|
|
||
|
STDMETHODIMP CMultiselectMoveDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
|
||
|
{
|
||
|
if ((pFormatEtc == NULL) || (pMedium == NULL))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
|
||
|
{
|
||
|
return DV_E_TYMED;
|
||
|
}
|
||
|
|
||
|
// we support only one clipboard format
|
||
|
pMedium->tymed = TYMED_HGLOBAL;
|
||
|
pMedium->pUnkForRelease = NULL;
|
||
|
if (pFormatEtc->cfFormat != m_cfDsObjectNames)
|
||
|
{
|
||
|
return DV_E_FORMATETC;
|
||
|
}
|
||
|
|
||
|
// make a deep copy of the cached data
|
||
|
pMedium->hGlobal = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
|
||
|
m_nDSObjCachedBytes);
|
||
|
if (pMedium->hGlobal == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
memcpy(pMedium->hGlobal, m_pDSObjCached, m_nDSObjCachedBytes);
|
||
|
pMedium->tymed = TYMED_HGLOBAL;
|
||
|
pMedium->pUnkForRelease = NULL;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CMultiselectMoveDataObject::Init(
|
||
|
IN CObjectNamesFormatCracker* pObjectNamesFormatPaste,
|
||
|
IN CMultiselectMoveHandler* pMoveHandler,
|
||
|
IN CDSComponentData* pCD)
|
||
|
{
|
||
|
_Clear();
|
||
|
|
||
|
|
||
|
|
||
|
// figure out how much storage we need
|
||
|
|
||
|
//
|
||
|
// this loop is to calc how much storage we need.
|
||
|
//
|
||
|
UINT nPasteCount = pObjectNamesFormatPaste->GetCount();
|
||
|
UINT nSuccessfulPasteCount = 0;
|
||
|
size_t cbStorage = 0;
|
||
|
for (UINT i=0; i<nPasteCount; i++)
|
||
|
{
|
||
|
if (pMoveHandler->WasItemMoved(i))
|
||
|
{
|
||
|
nSuccessfulPasteCount++;
|
||
|
cbStorage += (wcslen(pObjectNamesFormatPaste->GetClass(i)) + 1 +
|
||
|
wcslen(pObjectNamesFormatPaste->GetName(i)) + 1) * sizeof(WCHAR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nSuccessfulPasteCount == 0)
|
||
|
{
|
||
|
// no items were successfully pasted
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
// NOTICE: contains already a DSOBJECT embedded struct, so we subtract 1
|
||
|
DWORD cbStruct = sizeof(DSOBJECTNAMES) +
|
||
|
((nSuccessfulPasteCount - 1) * sizeof(DSOBJECT));
|
||
|
|
||
|
//
|
||
|
// Allocate the needed storage
|
||
|
//
|
||
|
m_pDSObjCached = (LPDSOBJECTNAMES)malloc(cbStruct + cbStorage);
|
||
|
|
||
|
if (m_pDSObjCached == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
m_nDSObjCachedBytes = static_cast<ULONG>(cbStruct + cbStorage);
|
||
|
|
||
|
switch (pCD->QuerySnapinType())
|
||
|
{
|
||
|
case SNAPINTYPE_DS:
|
||
|
m_pDSObjCached->clsidNamespace = CLSID_DSSnapin;
|
||
|
break;
|
||
|
case SNAPINTYPE_SITE:
|
||
|
m_pDSObjCached->clsidNamespace = CLSID_SiteSnapin;
|
||
|
break;
|
||
|
default:
|
||
|
m_pDSObjCached->clsidNamespace = CLSID_NULL;
|
||
|
}
|
||
|
|
||
|
m_pDSObjCached->cItems = nSuccessfulPasteCount;
|
||
|
DWORD NextOffset = cbStruct;
|
||
|
UINT index = 0;
|
||
|
for (i=0; i<nPasteCount; i++)
|
||
|
{
|
||
|
if (pMoveHandler->WasItemMoved(i))
|
||
|
{
|
||
|
//
|
||
|
// Set the data from the node and node data
|
||
|
//
|
||
|
|
||
|
size_t nNameLen = wcslen(pObjectNamesFormatPaste->GetName(i));
|
||
|
size_t nClassLen = wcslen(pObjectNamesFormatPaste->GetClass(i));
|
||
|
|
||
|
ASSERT((nNameLen > 0) && (nClassLen > 0));
|
||
|
|
||
|
m_pDSObjCached->aObjects[index].dwFlags = pObjectNamesFormatPaste->IsContainer(i) ? DSOBJECT_ISCONTAINER : 0;
|
||
|
m_pDSObjCached->aObjects[index].dwProviderFlags = (pCD->IsAdvancedView()) ?
|
||
|
DSPROVIDER_ADVANCED : 0;
|
||
|
m_pDSObjCached->aObjects[index].offsetName = NextOffset;
|
||
|
m_pDSObjCached->aObjects[index].offsetClass = static_cast<ULONG>(NextOffset +
|
||
|
(nNameLen + 1) * sizeof(WCHAR));
|
||
|
|
||
|
_tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetName(i));
|
||
|
NextOffset += static_cast<ULONG>((nNameLen + 1) * sizeof(WCHAR));
|
||
|
|
||
|
_tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetClass(i));
|
||
|
NextOffset += static_cast<ULONG>((nClassLen + 1) * sizeof(WCHAR));
|
||
|
|
||
|
index++;
|
||
|
} // if
|
||
|
} // for
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
void EscapeFilterElement(PCWSTR pszElement, CString& refszEscapedElement)
|
||
|
{
|
||
|
// do LDAP escaping (as per RFC 2254)
|
||
|
for (const WCHAR* pChar = pszElement; (*pChar) != NULL; pChar++)
|
||
|
{
|
||
|
switch (*pChar)
|
||
|
{
|
||
|
case L'*':
|
||
|
refszEscapedElement += L"\\2a";
|
||
|
break;
|
||
|
case L'(':
|
||
|
refszEscapedElement += L"\\28";
|
||
|
break;
|
||
|
case L')':
|
||
|
refszEscapedElement += L"\\29";
|
||
|
break;
|
||
|
case L'\\':
|
||
|
refszEscapedElement += L"\\5c";
|
||
|
break;
|
||
|
default:
|
||
|
refszEscapedElement += (*pChar);
|
||
|
} // switch
|
||
|
} // for
|
||
|
}
|