//+------------------------------------------------------------------------- // // 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"; //+------------------------------------------------------------------------- // // 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(CComBSTR(lpszLdapPath), ADS_SETTYPE_FULL); RETURN_IF_FAILED(hr); return pathCracker.Retrieve(ADS_FORMAT_SERVER, pbstrServerName); } BOOL StripADsIPath( LPCWSTR lpszPath, CString& strref, bool bUseEscapedMode /* = true */) { CPathCracker pathCracker; return StripADsIPath(lpszPath, strref, pathCracker, bUseEscapedMode); } BOOL StripADsIPath( LPCWSTR lpszPath, CString& strref, CPathCracker& pathCracker, bool bUseEscapedMode /* = true */) { if (lpszPath == NULL) { strref = L""; return FALSE; } if (wcslen(lpszPath) == 0) { strref = lpszPath; return FALSE; } if ( bUseEscapedMode ) pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON); pathCracker.SetDisplayType(ADS_DISPLAY_FULL); HRESULT hr = pathCracker.Set(CComBSTR(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; hr = pdo->GetData(&formatetc, &stgmedium); if (FAILED(hr)) return hr; PDS_SELECTION_LIST pSelList = (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal); if (!pSelList) { ReleaseStgMedium(&stgmedium); 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(); } } // NTRAID#NTBUG9-701373-2002/09/09-artm GlobalUnlock(stgmedium.hGlobal); ReleaseStgMedium(&stgmedium); 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. // // NTRAID#NTBUG9-639455-2002/06/13-artm CComPtr pDsObjectPicker; 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 the user hit Cancel, hr == S_FALSE // if (SUCCEEDED(hr) && hr != S_FALSE) { ASSERT(pdo); // Should never happen, indicates bug in object picker. hr = pdo->GetData(&formatetc, &stgmedium); } // NTRAID#NTBUG9-639455-2002/06/26-artm if (pdo) { pdo->Release(); pdo = NULL; } if (FAILED(hr) || hr == S_FALSE) { return hr; } PDS_SELECTION_LIST pSelList = (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal); if (!pSelList) { ReleaseStgMedium(&stgmedium); 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; } hr = AddDataObjListToGivenGroup(pNames, pSelList->aDsSelection[0].pwzADsPath, pSelList->aDsSelection[0].pwzName, hwnd, pComponentData); // NTRAID#NTBUG9-701373-2002/09/09-artm GlobalUnlock(stgmedium.hGlobal); ReleaseStgMedium(&stgmedium); 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 // PWSTR* pErrorArray = new PWSTR[pNames->GetCount()]; if (!pErrorArray) { return E_OUTOFMEMORY; } memset(pErrorArray, 0, pNames->GetCount() * sizeof(PWSTR)); 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); error = TRUE; 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(CComBSTR(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 (pErrorArray != NULL && pPathArray != NULL && pClassArray != NULL) { PWSTR pszErrMessage = 0; int iChar = cchLoadHrMsg(hr, &pszErrMessage, TRUE); if (pszErrMessage != NULL && iChar > 0) { // // this is a hack to get rid of two extra characters // at the end of the error message // size_t iLen = wcslen(pszErrMessage); //NTRAID#NTBUG9-571998-2002/03/10-jmessec possible underflow, not localization friendly pszErrMessage[iLen - 2] = L'\0'; pErrorArray[nErrorCount] = new WCHAR[wcslen(pszErrMessage) + 1]; if (pErrorArray[nErrorCount] != NULL) { wcscpy(pErrorArray[nErrorCount], pszErrMessage); } LocalFree(pszErrMessage); } 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(CComBSTR(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)); CThemeContextActivator activator; CMultiselectErrorDialog errDialog(pComponentData); VERIFY(SUCCEEDED(errDialog.Initialize(pPathArray, pClassArray, pErrorArray, 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 (pErrorArray != NULL) { for (UINT nIdx = 0; nIdx < pNames->GetCount(); nIdx++) { if (pErrorArray[nIdx] != NULL) { delete[] pErrorArray[nIdx]; } } delete[] pErrorArray; pErrorArray = 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; } // NTRAID#NTBUG9-472020-2002/01/16-ronmart-added comment block /******************************************************************* NAME: IsValidSiteName SYNOPSIS: Calls DnsValidateName to determine if the specified site name meets both size and format requirements RETURNS: (BOOL) TRUE if a valid site name, else FALSE. Callers can provide out flag pointers to learn more about the result of its internal call to DnsValidName NOTE: pfNonRfc is an optional out param that is set when DnsValidName fails because the name isn't RFC compliant. This function will still return TRUE because the caller can ignore this case, but it should typically check this value and prompt the user to continue. pfInvalidNameChar is an optional out param that is set when DnsValidateName fails because the site name contains an invalid character. HISTORY: ronmart 16-Jan-2002 Added pfInvalidNameChar support ********************************************************************/ BOOL IsValidSiteName( LPCTSTR lpctszSiteName, BOOL* pfNonRfc, BOOL* pfInvalidNameChar ) { if (NULL != pfNonRfc) *pfNonRfc = FALSE; if (NULL == lpctszSiteName) return FALSE; if (NULL != wcschr( lpctszSiteName, _T('.') )) { // NTRAID#NTBUG9-472020-2002/01/16-ronmart-set invalid char to display proper error message if(NULL != pfInvalidNameChar) *pfInvalidNameChar = TRUE; return FALSE; } // NTRAID#NTBUG9-472020-2002/01/16-ronmart-Changed from DnsValidateDnsName to DnsValidateName // with DnsNameDomainLabel flag. The old method was a macro that mapped to DnsValidateName // with DnsNameDomain as the 2nd param. DWORD dw = ::DnsValidateName_W( const_cast(lpctszSiteName), DnsNameDomainLabel ); switch (dw) { // NTRAID#NTBUG9-472020-2002/01/16-ronmart-If invalid char then fail and let the caller // know why so a proper error message can be displayed case DNS_ERROR_INVALID_NAME_CHAR: if (NULL != pfInvalidNameChar) *pfInvalidNameChar = TRUE; return FALSE; case DNS_ERROR_NON_RFC_NAME: if (NULL != pfNonRfc) *pfNonRfc = TRUE; break; 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; } // allocate SIDs hr = _SetSids(); if (FAILED(hr)) { TRACE(_T("failed on _SetSids()\n")); return hr; } // read info TRACE(_T("GetSDForDsObjectPath(%s)\n"), m_bstrObjectLdapPath); DWORD dwErr = ::GetDsObjectSD( m_bstrObjectLdapPath, &(m_pDacl), &(m_SDHolder.m_pSD)); TRACE(L"GetDsObjectSD() returned dwErr = 0x%x\n", dwErr); if (dwErr != ERROR_SUCCESS) { TRACE(L"GetDsObjectSD() 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 SetDsObjectDacl()\n"); dwErr = ::SetDsObjectDacl( (LPCWSTR)(BSTR)m_bstrObjectLdapPath, NewDacl.m_pAcl); TRACE(L"SetDsObjectDacl() returned dwErr = 0x%x\n", dwErr); if (dwErr != ERROR_SUCCESS) { TRACE(_T("SetDsObjectDacl() 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(CComBSTR(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) { // // JonN 5/16/01 392582 // DSAdmin: Message IDS_CONTAINS_CRITICALSYSOBJ isn't appropriate // when the object being deleted and the critical system object // are one and the same // // Allow subtree deletion where the root object is the only one // marked isCriticalSystemObject // if ( !lstrcmpi(lpszX500Path, PathColumn.pADsValues->CaseIgnoreString) ) { TRACE(_T("CheckForCriticalSystemObjectInSubtree: root is critical\n")); } else { // // Get the DN as a Windows style path // CComBSTR bstrLeaf; CPathCracker pathCrackerToo; HRESULT hrPathCracker = pathCrackerToo.Set(CComBSTR(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); } bRet = false; } Search.FreeColumn(&PathColumn); } Search.FreeColumn(&CriticalColumn); if (!bRet) break; } 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(CComBSTR(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 // JonN 12/19/01 267769 removed CDeleteDCDialog help class CDeleteDCDialog : public CDialog { // Construction public: CDeleteDCDialog(LPCTSTR lpszName, bool fIsComputer); // NTRAID#NTBUG9-267769-2001/06/06-lucios - Begin // JonN 12/19/01 267769 removed CDeleteDCDialog help // virtual void DoContextHelp(HWND hWndControl); // NTRAID#NTBUG9-267769-2001/06/06-lucios - End // 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() // JonN 12/19/01 267769 removed CDeleteDCDialog help CDeleteDCDialog::CDeleteDCDialog(LPCTSTR lpszADsPath, bool fIsComputer) : CDialog(IDD_DELETE_DC_COMPUTER, NULL) , m_strADsPath(lpszADsPath) , m_fIsComputer(fIsComputer) { } // NTRAID#NTBUG9-267769-2001/06/06-lucios - Begin // JonN 12/19/01 267769 removed CDeleteDCDialog help /* void CDeleteDCDialog::DoContextHelp(HWND hWndControl) { if (hWndControl) { ::WinHelp(hWndControl, DSADMIN_CONTEXT_HELP_FILE, HELP_WM_HELP, (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_DELETE_DC_COMPUTER); } } */ // NTRAID#NTBUG9-267769-2001/06/06-lucios - End BOOL CDeleteDCDialog::OnInitDialog() { // CODEWORK AfxInit? // JonN 12/19/01 267769 removed CDeleteDCDialog help CDialog::OnInitDialog(); CPathCracker pathCracker; pathCracker.Set(CComBSTR(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)) { // JonN 12/19/01 267769 removed CDeleteDCDialog help 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); // JonN 12/19/01 267769 removed CDeleteDCDialog help 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 = DSAdminOpenObject((LPWSTR)lpszADsPath, IID_IADs, (void **)&spIADs); CComVariant Var; if (SUCCEEDED(hr)) hr = spIADs->Get(CComBSTR(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 CThemeContextActivator activator; 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(CComBSTR(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() { CThemeContextActivator activator; 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()); CDSComponentData* pComponentData = GetComponentData(); if (pComponentData) { pComponentData->InvalidateSavedQueriesContainingObjects(szDeletedPathList); } } return hr; GetTransaction()->End(); return hr; } /////////////////////////////////////////////////////////////////////////// // CMultipleDeleteHandlerBase void CMultipleDeleteHandlerBase::Delete() { CThemeContextActivator activator; 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(CComBSTR(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; // JonN 1/28/02 507549 move connection object CString szClass; GetClassOfMovedItem( szClass ); CString szPath; GetItemPath( 0, szPath ); HRESULT hr2 = _CheckMovedObject(szClass, szPath); if (S_FALSE == hr2) { ReportMessageEx( GetParentHwnd(), IDS_MOVE_CONNECTION_TO_SELF ); return S_FALSE; } CComPtr spDSTargetIDispatch; HRESULT hr = m_spDSDestination->MoveHere(CComBSTR(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() { CThemeContextActivator activator; 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(CComBSTR(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; } // JonN 1/28/02 507549 move connection object HRESULT CMoveHandlerBase::_CheckMovedObject(LPCWSTR pwszClass, LPCWSTR pwszPath) { if (NULL == pwszClass || NULL == pwszPath) { ASSERT(FALSE); return S_OK; } if (_wcsicmp(pwszClass, L"nTDSConnection")) return S_OK; // not a connection // retrieve path to moved object CPathCracker pathCracker; CComBSTR sbstrDestPath = GetDestPath(); HRESULT hr = pathCracker.Set( sbstrDestPath, ADS_SETTYPE_FULL ); ASSERT( SUCCEEDED(hr) ); if ( !SUCCEEDED(hr) ) return S_OK; hr = pathCracker.Set( (CComBSTR)pwszPath, ADS_SETTYPE_DN ); ASSERT( SUCCEEDED(hr) ); if ( !SUCCEEDED(hr) ) return S_OK; CComBSTR sbstrADsPath; hr = pathCracker.Retrieve( ADS_FORMAT_X500, &sbstrADsPath ); ASSERT( SUCCEEDED(hr) ); if ( !SUCCEEDED(hr) ) return S_OK; // retrieve the fromServer attribute of the connection to be moved CComPtr spConnection; hr = DSAdminOpenObject(sbstrADsPath, IID_IADs, (void **)&spConnection, FALSE /*bServer*/); if (FAILED(hr)) return S_OK; // could not read fromServer attribute CComVariant svar; hr = spConnection->Get((CComBSTR)L"fromServer", &svar); if (FAILED(hr) || (VT_BSTR != svar.vt) || (NULL == svar.bstrVal)) return S_OK; hr = pathCracker.Set( sbstrDestPath, ADS_SETTYPE_FULL ); ASSERT( SUCCEEDED(hr) ); if ( !SUCCEEDED(hr) ) return S_OK; CComBSTR sbstrDestContainer; hr = pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrDestContainer ); ASSERT( SUCCEEDED(hr) ); if ( !SUCCEEDED(hr) ) return S_OK; if ( !_wcsicmp(sbstrDestContainer, svar.bstrVal) ) return S_FALSE; // same path return S_OK; } 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; } // switch return 1; // handled } HRESULT CMoveHandlerBase::_BrowseForDestination(LPCWSTR lpszDestinationPath) { CThemeContextActivator activator; 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(CComBSTR(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() { CThemeContextActivator activator; // 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(CComBSTR(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, DS_UI_NODE)) { // 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, DS_UI_NODE) ) { lpszStartingContainerPath = dynamic_cast(pContainerNode)->GetCookie()->GetPath(); nAdjustLevel = 1; } else if (IS_CLASS(pContainerNode, ROOT_UI_NODE) ) { 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; } //NTRAID#NTBUG9-571999-2002/03/10-jmessec If pMedium->hGlobal already pointed to allocated memory, here is a memory leak // 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)); //NTRAID#NTBUG9-567482-2002/03/10-jmessec ASSERT not handled with code 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 }