//+------------------------------------------------------------------------- // // 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 #include #include // LDAP_MATCHING_RULE_BIT_AND_W #include // UF_SERVER_TRUST_ACCOUNT #include // DsRemoveDsServer #include // 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(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* pObject; CComObject::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(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((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((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((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 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(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 spDSTargetIDispatch; HRESULT hr = m_spDSDestination->MoveHere(const_cast(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 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 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 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(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 spDSDestination; HRESULT hr = DSAdminOpenObject(strTargetContainer, IID_IADsContainer, (void **)&spDSDestination, FALSE /*bServer*/); if (FAILED(hr)) { CPathCracker pathCracker; pathCracker.Set(const_cast((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 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(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(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 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(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(pContainerNode)->GetCookie()->GetPath(); nAdjustLevel = 1; } else if (IS_CLASS(*pContainerNode, CRootNode) ) { lpszStartingContainerPath = dynamic_cast(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* pObject; CComObject::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(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; iWasItemMoved(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(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; iWasItemMoved(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(NextOffset + (nNameLen + 1) * sizeof(WCHAR)); _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetName(i)); NextOffset += static_cast((nNameLen + 1) * sizeof(WCHAR)); _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetClass(i)); NextOffset += static_cast((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 }