Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4086 lines
108 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1999
  5. //
  6. // File: dsUtil.cpp
  7. //
  8. // Contents: Utility functions
  9. //
  10. // History: 08-Nov-99 JeffJon Created
  11. //
  12. //
  13. //--------------------------------------------------------------------------
  14. #include "stdafx.h"
  15. #include "dsutil.h"
  16. #include "dssnap.h"
  17. #include "gsz.h"
  18. #include "helpids.h"
  19. #include "querysup.h"
  20. #include "wininet.h"
  21. #include <dnsapi.h>
  22. #include <objsel.h>
  23. #include <ntldap.h> // LDAP_MATCHING_RULE_BIT_AND_W
  24. #include <lmaccess.h> // UF_SERVER_TRUST_ACCOUNT
  25. #include <ntdsapi.h> // DsRemoveDsServer
  26. #include <ntsecapi.h> // Lsa*
  27. UINT g_cfDsObjectPicker = RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
  28. //
  29. // Common DS strings
  30. //
  31. PCWSTR g_pszAllowedAttributesEffective = L"allowedAttributesEffective";
  32. PCWSTR g_pszPwdLastSet = L"pwdLastSet";
  33. //
  34. // This is a wrapper for ADsOpenObject. It gives DSAdmin a single point to change
  35. // global flags that are passed to ADsOpenObject without have to search and replace
  36. // all occurrences in the code
  37. //
  38. HRESULT DSAdminOpenObject(PCWSTR pszPath,
  39. REFIID refIID,
  40. PVOID* ppObject,
  41. BOOL bServer)
  42. {
  43. HRESULT hr = S_OK;
  44. DWORD dwFlags = ADS_SECURE_AUTHENTICATION;
  45. if (bServer)
  46. {
  47. //
  48. // If we know we are connecting to a specific server and not domain in general
  49. // then pass the ADS_SERVER_BIND flag to save ADSI the trouble of figuring it out
  50. //
  51. dwFlags |= ADS_SERVER_BIND;
  52. }
  53. hr = ADsOpenObject((LPWSTR)pszPath, NULL, NULL, dwFlags, refIID, ppObject);
  54. return hr;
  55. }
  56. //+-------------------------------------------------------------------------
  57. //
  58. // Function: GetServerFromLDAPPath
  59. //
  60. // Synopsis: Gets the server portion of an LDAP Path
  61. //
  62. // In:
  63. // LPCWSTR - pointer to string to convert
  64. //
  65. // Out:
  66. // BSTR* - pointer to a BSTR containing the server name
  67. //
  68. // Return:
  69. // HRESULT - whether the operation completed successfully
  70. //
  71. //--------------------------------------------------------------------------
  72. HRESULT GetServerFromLDAPPath(IN LPCWSTR lpszLdapPath, OUT BSTR* pbstrServerName)
  73. {
  74. if (pbstrServerName == NULL)
  75. {
  76. return E_POINTER;
  77. }
  78. else if (*pbstrServerName != NULL)
  79. {
  80. ::SysFreeString(*pbstrServerName);
  81. *pbstrServerName = NULL;
  82. }
  83. CPathCracker pathCracker;
  84. HRESULT hr = pathCracker.Set((LPWSTR)lpszLdapPath, ADS_SETTYPE_FULL);
  85. RETURN_IF_FAILED(hr);
  86. return pathCracker.Retrieve(ADS_FORMAT_SERVER, pbstrServerName);
  87. }
  88. BOOL StripADsIPath(LPCWSTR lpszPath, CString& strref, bool bUseEscapedMode /* = true */)
  89. {
  90. if (lpszPath == NULL)
  91. {
  92. strref = L"";
  93. return FALSE;
  94. }
  95. if (wcslen(lpszPath) == 0)
  96. {
  97. strref = lpszPath;
  98. return FALSE;
  99. }
  100. CPathCracker pathCracker;
  101. if ( bUseEscapedMode )
  102. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
  103. pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  104. HRESULT hr = pathCracker.Set((PWSTR)lpszPath, ADS_SETTYPE_FULL);
  105. if (FAILED(hr))
  106. {
  107. TRACE(_T("StripADsIPath, IADsPathname::Set returned error 0x%x\n"), hr);
  108. strref = lpszPath;
  109. return FALSE;
  110. }
  111. CComBSTR bsX500DN;
  112. (void) pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bsX500DN);
  113. strref = bsX500DN;
  114. return TRUE;
  115. }
  116. ////////////////////////////////////////////////////////////////////////
  117. // support routines for Add To Group function
  118. void RemovePortifPresent(CString *csGroup)
  119. {
  120. CString x, y;
  121. int n = csGroup->Find (L":3268");
  122. if (n > 0) {
  123. x = csGroup->Left(n);
  124. y = csGroup->Right(csGroup->GetLength() - (n+5));
  125. *csGroup = x+y;
  126. }
  127. }
  128. #if (FALSE)
  129. HRESULT AddDataObjListToGroup(IN CObjectNamesFormatCracker* pNames,
  130. IN HWND hwnd)
  131. {
  132. HRESULT hr = S_OK;
  133. TRACE (_T("CDSContextMenu::AddToGroup\n"));
  134. STGMEDIUM stgmedium =
  135. {
  136. TYMED_HGLOBAL,
  137. NULL,
  138. NULL
  139. };
  140. FORMATETC formatetc =
  141. {
  142. (CLIPFORMAT)g_cfDsObjectPicker,
  143. NULL,
  144. DVASPECT_CONTENT,
  145. -1,
  146. TYMED_HGLOBAL
  147. };
  148. // algorithm
  149. // examine selection, figure out classes
  150. // figure out what groups are possible
  151. // call object picker, get a group
  152. // for each object in selection
  153. // if container
  154. // procees_container()
  155. // else
  156. // process_leaf()
  157. //
  158. //
  159. // Create an instance of the object picker.
  160. //
  161. IDsObjectPicker * pDsObjectPicker = NULL;
  162. hr = CoCreateInstance(CLSID_DsObjectPicker,
  163. NULL,
  164. CLSCTX_INPROC_SERVER,
  165. IID_IDsObjectPicker,
  166. (void **) &pDsObjectPicker);
  167. if (FAILED(hr))
  168. return(hr);
  169. //
  170. // Prepare to initialize the object picker.
  171. //
  172. // first, get the name of DC that we are talking to.
  173. CComBSTR bstrDC;
  174. LPCWSTR lpszPath = pNames->GetName(0);
  175. GetServerFromLDAPPath(lpszPath, &bstrDC);
  176. //
  177. // Set up the array of scope initializer structures.
  178. //
  179. static const int SCOPE_INIT_COUNT = 1;
  180. DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
  181. int scopeindex = 0;
  182. ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
  183. //
  184. //
  185. // The domain to which the target computer is joined. Note we're
  186. // combining two scope types into flType here for convenience.
  187. //
  188. aScopeInit[scopeindex].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  189. aScopeInit[scopeindex].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
  190. aScopeInit[scopeindex].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE |
  191. DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  192. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  193. aScopeInit[scopeindex].FilterFlags.Uplevel.flNativeModeOnly =
  194. DSOP_FILTER_GLOBAL_GROUPS_SE
  195. | DSOP_FILTER_UNIVERSAL_GROUPS_SE
  196. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
  197. | DSOP_FILTER_GLOBAL_GROUPS_DL
  198. | DSOP_FILTER_UNIVERSAL_GROUPS_DL
  199. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
  200. | DSOP_FILTER_BUILTIN_GROUPS;
  201. aScopeInit[scopeindex].FilterFlags.Uplevel.flMixedModeOnly =
  202. DSOP_FILTER_GLOBAL_GROUPS_SE
  203. | DSOP_FILTER_UNIVERSAL_GROUPS_SE
  204. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
  205. | DSOP_FILTER_GLOBAL_GROUPS_DL
  206. | DSOP_FILTER_UNIVERSAL_GROUPS_DL
  207. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
  208. | DSOP_FILTER_BUILTIN_GROUPS;
  209. aScopeInit[scopeindex].pwzDcName = bstrDC;
  210. //
  211. // Put the scope init array into the object picker init array
  212. //
  213. DSOP_INIT_INFO InitInfo;
  214. ZeroMemory(&InitInfo, sizeof(InitInfo));
  215. InitInfo.cbSize = sizeof(InitInfo);
  216. //
  217. // The pwzTargetComputer member allows the object picker to be
  218. // retargetted to a different computer. It will behave as if it
  219. // were being run ON THAT COMPUTER.
  220. //
  221. InitInfo.pwzTargetComputer = bstrDC;
  222. InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
  223. InitInfo.aDsScopeInfos = aScopeInit;
  224. InitInfo.flOptions = 0;
  225. //
  226. // Note object picker makes its own copy of InitInfo. Also note
  227. // that Initialize may be called multiple times, last call wins.
  228. //
  229. hr = pDsObjectPicker->Initialize(&InitInfo);
  230. if (FAILED(hr))
  231. {
  232. ULONG i;
  233. for (i = 0; i < SCOPE_INIT_COUNT; i++)
  234. {
  235. if (FAILED(InitInfo.aDsScopeInfos[i].hr))
  236. {
  237. TRACE(_T("Initialization failed because of scope %u\n"), i);
  238. }
  239. }
  240. ReportErrorEx (hwnd, IDS_OBJECT_PICKER_INIT_FAILED, hr,
  241. MB_OK | MB_ICONERROR, NULL, 0);
  242. return hr;
  243. }
  244. IDataObject *pdo = NULL;
  245. //
  246. // Invoke the modal dialog.
  247. //
  248. hr = pDsObjectPicker->InvokeDialog(hwnd, &pdo);
  249. if (FAILED(hr))
  250. return(hr);
  251. //
  252. // If the user hit Cancel, hr == S_FALSE
  253. //
  254. if (hr == S_FALSE)
  255. return hr;
  256. bool fGotStgMedium = false;
  257. hr = pdo->GetData(&formatetc, &stgmedium);
  258. if (FAILED(hr))
  259. return hr;
  260. fGotStgMedium = true;
  261. PDS_SELECTION_LIST pSelList =
  262. (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
  263. if (!pSelList)
  264. {
  265. TRACE(_T("GlobalLock error %u\n"), GetLastError());
  266. //
  267. // REVIEW_JEFFJON : should probably put some kind of error message
  268. // here even though we ignore the return value
  269. //
  270. return E_FAIL;
  271. }
  272. if (pDsObjectPicker)
  273. {
  274. pDsObjectPicker->Release();
  275. }
  276. /////////////////////////////////////////////////////////////
  277. UINT index;
  278. DWORD cModified = 0;
  279. CString csClass;
  280. CString objDN;
  281. IDirectoryObject * pObj = NULL;
  282. INT answer = IDYES;
  283. BOOL error = FALSE;
  284. BOOL partial_success = FALSE;
  285. CWaitCursor CWait;
  286. CString csPath;
  287. if (pSelList != NULL)
  288. {
  289. TRACE(_T("AddToGroup: binding to group path is %s\n"), pSelList->aDsSelection[0].pwzADsPath);
  290. CString csGroup = pSelList->aDsSelection[0].pwzADsPath;
  291. RemovePortifPresent(&csGroup);
  292. hr = DSAdminOpenObject(csGroup,
  293. IID_IDirectoryObject,
  294. (void **)&pObj,
  295. FALSE /*bServer*/);
  296. if (FAILED(hr))
  297. {
  298. PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)pSelList->aDsSelection[0].pwzName};
  299. ReportErrorEx (hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
  300. MB_OK | MB_ICONERROR, apv, 1);
  301. goto ExitGracefully;
  302. }
  303. for (index = 0; index < pNames->GetCount(); index++)
  304. {
  305. csPath = pNames->GetName(index);
  306. TRACE(_T("AddToGroup: object path is %s\n"), csPath);
  307. csClass = pNames->GetClass(index);
  308. TRACE(_T("AddToGroup: object class is %s\n"), csClass);
  309. ADS_ATTR_INFO Attrinfo;
  310. ZeroMemory (&Attrinfo, sizeof (ADS_ATTR_INFO));
  311. PADS_ATTR_INFO pAttrs = &Attrinfo;
  312. Attrinfo.pszAttrName = L"member";
  313. Attrinfo.dwADsType = ADSTYPE_DN_STRING;
  314. Attrinfo.dwControlCode = ADS_ATTR_APPEND;
  315. ADSVALUE Value;
  316. pAttrs->pADsValues = &Value;
  317. pAttrs->dwNumValues = 1;
  318. // make sure there's no strange escaping in the path
  319. CComBSTR bstrPath;
  320. CPathCracker pathCracker;
  321. pathCracker.Set((LPTSTR)(LPCTSTR)csPath, ADS_SETTYPE_FULL);
  322. pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  323. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF);
  324. pathCracker.Retrieve( ADS_FORMAT_X500, &bstrPath);
  325. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
  326. StripADsIPath(bstrPath, objDN);
  327. Value.DNString = (LPWSTR)(LPCWSTR)objDN;
  328. Value.dwType = ADSTYPE_DN_STRING;
  329. hr = pObj->SetObjectAttributes(pAttrs, 1, &cModified);
  330. if (FAILED(hr))
  331. {
  332. error = TRUE;
  333. // prep for display by getting obj name
  334. pathCracker.Set((LPWSTR)pNames->GetName(index), ADS_SETTYPE_FULL);
  335. pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  336. CComBSTR ObjName;
  337. pathCracker.GetElement( 0, &ObjName );
  338. PVOID apv[2] = {(BSTR)(LPWSTR)(LPCWSTR)ObjName,
  339. (BSTR)(LPWSTR)(LPCWSTR)pSelList->aDsSelection[0].pwzName};
  340. if ((pNames->GetCount() - index) == 1)
  341. {
  342. ReportErrorEx (hwnd,IDS_12_MEMBER_ADD_FAILED,hr,
  343. MB_OK | MB_ICONERROR, apv, 2);
  344. }
  345. else
  346. {
  347. int answer1;
  348. answer1 = ReportErrorEx (hwnd,IDS_12_MULTI_MEMBER_ADD_FAILED,hr,
  349. MB_YESNO | MB_ICONERROR, apv, 2);
  350. if (answer1 == IDNO)
  351. {
  352. answer = IDCANCEL;
  353. }
  354. }
  355. }
  356. else
  357. {
  358. partial_success = TRUE;
  359. }
  360. if (answer == IDCANCEL)
  361. {
  362. goto ExitGracefully;
  363. }
  364. }
  365. ExitGracefully:
  366. if( error )
  367. {
  368. if (partial_success == TRUE)
  369. {
  370. ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_PARTIALLY_COMPLETED, S_OK,
  371. MB_OK | MB_ICONINFORMATION, NULL, 0);
  372. }
  373. else
  374. {
  375. ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_FAILED, S_OK,
  376. MB_OK | MB_ICONINFORMATION, NULL, 0);
  377. }
  378. }
  379. else if( partial_success == TRUE )
  380. {
  381. ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_COMPLETED, S_OK,
  382. MB_OK | MB_ICONINFORMATION, NULL, 0);
  383. }
  384. else
  385. {
  386. //else we did nothing and appropriate messages are shown already
  387. ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_FAILED, S_OK,
  388. MB_OK | MB_ICONINFORMATION, NULL, 0);
  389. }
  390. if (pObj)
  391. {
  392. pObj->Release();
  393. }
  394. }
  395. return hr;
  396. }
  397. #endif
  398. HRESULT AddDataObjListToGroup(IN CObjectNamesFormatCracker* pNames,
  399. IN HWND hwnd,
  400. IN CDSComponentData* pComponentData)
  401. {
  402. HRESULT hr = S_OK;
  403. TRACE (_T("CDSContextMenu::AddToGroup\n"));
  404. STGMEDIUM stgmedium =
  405. {
  406. TYMED_HGLOBAL,
  407. NULL,
  408. NULL
  409. };
  410. FORMATETC formatetc =
  411. {
  412. (CLIPFORMAT)g_cfDsObjectPicker,
  413. NULL,
  414. DVASPECT_CONTENT,
  415. -1,
  416. TYMED_HGLOBAL
  417. };
  418. // algorithm
  419. // examine selection, figure out classes
  420. // figure out what groups are possible
  421. // call object picker, get a group
  422. // for each object in selection
  423. // if container
  424. // procees_container()
  425. // else
  426. // process_leaf()
  427. //
  428. //
  429. // Create an instance of the object picker.
  430. //
  431. IDsObjectPicker * pDsObjectPicker = NULL;
  432. hr = CoCreateInstance(CLSID_DsObjectPicker,
  433. NULL,
  434. CLSCTX_INPROC_SERVER,
  435. IID_IDsObjectPicker,
  436. (void **) &pDsObjectPicker);
  437. if (FAILED(hr))
  438. return(hr);
  439. //
  440. // Prepare to initialize the object picker.
  441. //
  442. // first, get the name of DC that we are talking to.
  443. CComBSTR bstrDC;
  444. LPCWSTR lpszPath = pNames->GetName(0);
  445. GetServerFromLDAPPath(lpszPath, &bstrDC);
  446. //
  447. // Set up the array of scope initializer structures.
  448. //
  449. static const int SCOPE_INIT_COUNT = 1;
  450. DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
  451. int scopeindex = 0;
  452. ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
  453. //
  454. //
  455. // The domain to which the target computer is joined. Note we're
  456. // combining two scope types into flType here for convenience.
  457. //
  458. aScopeInit[scopeindex].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  459. aScopeInit[scopeindex].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
  460. aScopeInit[scopeindex].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE |
  461. DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  462. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  463. aScopeInit[scopeindex].FilterFlags.Uplevel.flNativeModeOnly =
  464. DSOP_FILTER_GLOBAL_GROUPS_SE
  465. | DSOP_FILTER_UNIVERSAL_GROUPS_SE
  466. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
  467. | DSOP_FILTER_GLOBAL_GROUPS_DL
  468. | DSOP_FILTER_UNIVERSAL_GROUPS_DL
  469. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
  470. | DSOP_FILTER_BUILTIN_GROUPS;
  471. aScopeInit[scopeindex].FilterFlags.Uplevel.flMixedModeOnly =
  472. DSOP_FILTER_GLOBAL_GROUPS_SE
  473. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
  474. | DSOP_FILTER_GLOBAL_GROUPS_DL
  475. | DSOP_FILTER_UNIVERSAL_GROUPS_DL
  476. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL
  477. | DSOP_FILTER_BUILTIN_GROUPS;
  478. aScopeInit[scopeindex].pwzDcName = bstrDC;
  479. //
  480. // Put the scope init array into the object picker init array
  481. //
  482. DSOP_INIT_INFO InitInfo;
  483. ZeroMemory(&InitInfo, sizeof(InitInfo));
  484. InitInfo.cbSize = sizeof(InitInfo);
  485. //
  486. // The pwzTargetComputer member allows the object picker to be
  487. // retargetted to a different computer. It will behave as if it
  488. // were being run ON THAT COMPUTER.
  489. //
  490. InitInfo.pwzTargetComputer = bstrDC;
  491. InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
  492. InitInfo.aDsScopeInfos = aScopeInit;
  493. InitInfo.flOptions = 0;
  494. //
  495. // Note object picker makes its own copy of InitInfo. Also note
  496. // that Initialize may be called multiple times, last call wins.
  497. //
  498. hr = pDsObjectPicker->Initialize(&InitInfo);
  499. if (FAILED(hr)) {
  500. ULONG i;
  501. for (i = 0; i < SCOPE_INIT_COUNT; i++) {
  502. if (FAILED(InitInfo.aDsScopeInfos[i].hr)) {
  503. TRACE(_T("Initialization failed because of scope %u\n"), i);
  504. }
  505. }
  506. ReportErrorEx (hwnd, IDS_OBJECT_PICKER_INIT_FAILED, hr,
  507. MB_OK | MB_ICONERROR, NULL, 0);
  508. return hr;
  509. }
  510. IDataObject *pdo = NULL;
  511. //
  512. // Invoke the modal dialog.
  513. //
  514. hr = pDsObjectPicker->InvokeDialog(hwnd, &pdo);
  515. if (FAILED(hr))
  516. return(hr);
  517. //
  518. // If the user hit Cancel, hr == S_FALSE
  519. //
  520. if (hr == S_FALSE)
  521. return hr;
  522. bool fGotStgMedium = false;
  523. hr = pdo->GetData(&formatetc, &stgmedium);
  524. if (FAILED(hr))
  525. return hr;
  526. fGotStgMedium = true;
  527. PDS_SELECTION_LIST pSelList =
  528. (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
  529. if (!pSelList)
  530. {
  531. TRACE(_T("GlobalLock error %u\n"), GetLastError());
  532. //
  533. // REVIEW_JEFFJON : should probably put some kind of error message
  534. // here even though we ignore the return value
  535. //
  536. return E_FAIL;
  537. }
  538. if (pDsObjectPicker) {
  539. pDsObjectPicker->Release();
  540. }
  541. hr = AddDataObjListToGivenGroup(pNames,
  542. pSelList->aDsSelection[0].pwzADsPath,
  543. pSelList->aDsSelection[0].pwzName,
  544. hwnd,
  545. pComponentData);
  546. ::GlobalFree(stgmedium.hGlobal);
  547. return hr;
  548. }
  549. HRESULT AddDataObjListToGivenGroup(CObjectNamesFormatCracker * pNames,
  550. LPCWSTR lpszGroupLDapPath,
  551. LPCWSTR lpszGroupName,
  552. HWND hwnd,
  553. CDSComponentData* pComponentData)
  554. {
  555. HRESULT hr = S_OK;
  556. UINT index = 0;
  557. DWORD cModified = 0;
  558. CString csClass;
  559. CString objDN;
  560. IDirectoryObject * pObj = NULL;
  561. BOOL error = FALSE;
  562. BOOL partial_success = FALSE;
  563. CWaitCursor CWait;
  564. CString csPath;
  565. //
  566. // Prepare error structures for use with the multi-operation error dialog
  567. // These arrays may not be completely full depending on the number of errors
  568. // that occurr
  569. //
  570. HRESULT* phrArray = new HRESULT[pNames->GetCount()];
  571. if (!phrArray)
  572. {
  573. return E_OUTOFMEMORY;
  574. }
  575. memset(phrArray, 0, pNames->GetCount() * sizeof(HRESULT));
  576. PWSTR* pPathArray = new PWSTR[pNames->GetCount()];
  577. if (!pPathArray)
  578. {
  579. return E_OUTOFMEMORY;
  580. }
  581. memset(pPathArray, 0, pNames->GetCount() * sizeof(PWSTR));
  582. PWSTR* pClassArray = new PWSTR[pNames->GetCount()];
  583. if (!pClassArray)
  584. {
  585. return E_OUTOFMEMORY;
  586. }
  587. memset(pClassArray, 0, pNames->GetCount() * sizeof(PWSTR));
  588. UINT nErrorCount = 0;
  589. TRACE(_T("AddToGroup: binding to group path is %s\n"), lpszGroupLDapPath);
  590. CString csGroup = lpszGroupLDapPath;
  591. RemovePortifPresent(&csGroup);
  592. hr = DSAdminOpenObject(csGroup,
  593. IID_IDirectoryObject,
  594. (void **)&pObj,
  595. FALSE /*bServer*/);
  596. if (FAILED(hr))
  597. {
  598. PVOID apv[1] = {(BSTR)(LPWSTR)lpszGroupName};
  599. ReportErrorEx (hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
  600. MB_OK | MB_ICONERROR, apv, 1);
  601. goto ExitGracefully;
  602. }
  603. for (index = 0; index < pNames->GetCount(); index++)
  604. {
  605. // make sure there's no strange escaping in the path
  606. CComBSTR bstrDN;
  607. csPath = pNames->GetName(index);
  608. TRACE(_T("AddToGroup: object path is %s\n"), csPath);
  609. csClass = pNames->GetClass(index);
  610. TRACE(_T("AddToGroup: object class is %s\n"), csClass);
  611. ADS_ATTR_INFO Attrinfo;
  612. ZeroMemory (&Attrinfo, sizeof (ADS_ATTR_INFO));
  613. PADS_ATTR_INFO pAttrs = &Attrinfo;
  614. Attrinfo.pszAttrName = L"member";
  615. Attrinfo.dwADsType = ADSTYPE_DN_STRING;
  616. Attrinfo.dwControlCode = ADS_ATTR_APPEND;
  617. ADSVALUE Value;
  618. pAttrs->pADsValues = &Value;
  619. pAttrs->dwNumValues = 1;
  620. CPathCracker pathCracker;
  621. pathCracker.Set((LPTSTR)(LPCTSTR)csPath, ADS_SETTYPE_FULL);
  622. pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  623. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF);
  624. pathCracker.Retrieve( ADS_FORMAT_X500_DN, &bstrDN);
  625. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_ON);
  626. objDN = bstrDN;
  627. Value.DNString = (LPWSTR)(LPCWSTR)objDN;
  628. Value.dwType = ADSTYPE_DN_STRING;
  629. hr = pObj->SetObjectAttributes(pAttrs, 1, &cModified);
  630. if (FAILED(hr))
  631. {
  632. error = TRUE;
  633. if (pNames->GetCount() > 1)
  634. {
  635. if (phrArray != NULL &&
  636. pPathArray != NULL &&
  637. pClassArray != NULL)
  638. {
  639. phrArray[nErrorCount] = hr;
  640. pPathArray[nErrorCount] = new WCHAR[wcslen(objDN) + 1];
  641. if (pPathArray[nErrorCount] != NULL)
  642. {
  643. wcscpy(pPathArray[nErrorCount], objDN);
  644. }
  645. pClassArray[nErrorCount] = new WCHAR[wcslen(csClass) + 1];
  646. if (pClassArray[nErrorCount] != NULL)
  647. {
  648. wcscpy(pClassArray[nErrorCount], csClass);
  649. }
  650. nErrorCount++;
  651. }
  652. }
  653. else
  654. {
  655. //
  656. // prep for display by getting obj name
  657. //
  658. CPathCracker pathCrackerToo;
  659. pathCrackerToo.Set((LPWSTR)pNames->GetName(index), ADS_SETTYPE_FULL);
  660. pathCrackerToo.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  661. CComBSTR ObjName;
  662. pathCrackerToo.GetElement( 0, &ObjName );
  663. PVOID apv[2] = {(BSTR)(LPWSTR)(LPCWSTR)ObjName,
  664. (BSTR)(LPWSTR)lpszGroupName};
  665. ReportErrorEx (hwnd,IDS_12_MEMBER_ADD_FAILED,hr,
  666. MB_OK | MB_ICONERROR, apv, 2);
  667. }
  668. }
  669. else
  670. {
  671. partial_success = TRUE;
  672. }
  673. } // for
  674. ExitGracefully:
  675. if (nErrorCount > 0 && pNames->GetCount() > 1)
  676. {
  677. //
  678. // Load the strings for the error dialog
  679. //
  680. CString szTitle;
  681. if (pComponentData->QuerySnapinType() == SNAPINTYPE_SITE)
  682. {
  683. VERIFY(szTitle.LoadString(IDS_SITESNAPINNAME));
  684. }
  685. else
  686. {
  687. VERIFY(szTitle.LoadString(IDS_DSSNAPINNAME));
  688. }
  689. CString szCaption;
  690. VERIFY(szCaption.LoadString(IDS_MULTI_ADDTOGROUP_ERROR_CAPTION));
  691. CString szHeader;
  692. VERIFY(szHeader.LoadString(IDS_COLUMN_NAME));
  693. CMultiselectErrorDialog errDialog(pComponentData);
  694. VERIFY(SUCCEEDED(errDialog.Initialize(pPathArray,
  695. pClassArray,
  696. phrArray,
  697. nErrorCount,
  698. szTitle,
  699. szCaption,
  700. szHeader)));
  701. errDialog.DoModal();
  702. }
  703. else if (nErrorCount == 0 && !error)
  704. {
  705. ReportErrorEx (hwnd, IDS_ADDTOGROUP_OPERATION_COMPLETED, S_OK,
  706. MB_OK | MB_ICONINFORMATION, NULL, 0);
  707. }
  708. else
  709. {
  710. //
  711. // Do nothing if it was single select and there was a failure
  712. // The error should have already been reported.
  713. //
  714. }
  715. if (pObj)
  716. {
  717. pObj->Release();
  718. }
  719. if (phrArray != NULL)
  720. {
  721. delete[] phrArray;
  722. phrArray = NULL;
  723. }
  724. if (pPathArray != NULL)
  725. {
  726. for (UINT nIdx = 0; nIdx < pNames->GetCount(); nIdx++)
  727. {
  728. if (pPathArray[nIdx] != NULL)
  729. {
  730. delete[] pPathArray[nIdx];
  731. }
  732. }
  733. delete[] pPathArray;
  734. pPathArray = NULL;
  735. }
  736. if (pClassArray != NULL)
  737. {
  738. for (UINT nIdx = 0; nIdx < pNames->GetCount(); nIdx++)
  739. {
  740. if (pClassArray[nIdx] != NULL)
  741. {
  742. delete[] pClassArray[nIdx];
  743. }
  744. }
  745. delete[] pClassArray;
  746. pClassArray = NULL;
  747. }
  748. return hr;
  749. }
  750. BOOL IsValidSiteName( LPCTSTR lpctszSiteName, BOOL* pfNonRfc )
  751. {
  752. if (NULL != pfNonRfc)
  753. *pfNonRfc = FALSE;
  754. if (NULL == lpctszSiteName)
  755. return FALSE;
  756. if (NULL != wcschr( lpctszSiteName, _T('.') ))
  757. return FALSE;
  758. DWORD dw = ::DnsValidateDnsName_W( const_cast<LPTSTR>(lpctszSiteName) );
  759. switch (dw)
  760. {
  761. case DNS_ERROR_NON_RFC_NAME:
  762. if (NULL != pfNonRfc)
  763. *pfNonRfc = TRUE;
  764. // fall through
  765. case DNS_ERROR_RCODE_NO_ERROR:
  766. break;
  767. default:
  768. return FALSE;
  769. }
  770. return TRUE;
  771. }
  772. /*******************************************************************
  773. NAME: GetAuthenticationID
  774. SYNOPSIS: Retrieves the UserName associated with the credentials
  775. currently being used for network access.
  776. (runas /netonly credentials)
  777. RETURNS: A win32 error code
  778. NOTE: String returned must be freed with LocalFree
  779. HISTORY:
  780. JeffreyS 05-Aug-1999 Created
  781. Modified by hiteshr to return credentials
  782. JeffJon 21-Nov-2000 Modified to return a win32 error
  783. ********************************************************************/
  784. ULONG
  785. GetAuthenticationID(LPWSTR *ppszResult)
  786. {
  787. ULONG ulReturn = 0;
  788. HANDLE hLsa;
  789. NTSTATUS Status = 0;
  790. *ppszResult = NULL;
  791. //
  792. // These LSA calls are delay-loaded from secur32.dll using the linker's
  793. // delay-load mechanism. Therefore, wrap with an exception handler.
  794. //
  795. __try
  796. {
  797. Status = LsaConnectUntrusted(&hLsa);
  798. if (Status == 0)
  799. {
  800. NEGOTIATE_CALLER_NAME_REQUEST Req = {0};
  801. PNEGOTIATE_CALLER_NAME_RESPONSE pResp;
  802. ULONG cbSize =0;
  803. NTSTATUS SubStatus =0;
  804. Req.MessageType = NegGetCallerName;
  805. Status = LsaCallAuthenticationPackage(
  806. hLsa,
  807. 0,
  808. &Req,
  809. sizeof(Req),
  810. (void**)&pResp,
  811. &cbSize,
  812. &SubStatus);
  813. if ((Status == 0) && (SubStatus == 0))
  814. {
  815. LocalAllocString(ppszResult,pResp->CallerName);
  816. LsaFreeReturnBuffer(pResp);
  817. }
  818. LsaDeregisterLogonProcess(hLsa);
  819. }
  820. ulReturn = LsaNtStatusToWinError(Status);
  821. }
  822. __except(EXCEPTION_EXECUTE_HANDLER)
  823. {
  824. }
  825. return ulReturn;
  826. }
  827. // IsLocalLogin
  828. //Function determines if the user has logged in as
  829. //Local user or to a domain
  830. BOOL IsLocalLogin( void )
  831. {
  832. DWORD nSize = 0;
  833. PWSTR pszUserName = NULL;
  834. if (ERROR_SUCCESS != GetAuthenticationID(&pszUserName))
  835. return false;
  836. CString strSamComName( pszUserName );
  837. int nPos = strSamComName.Find('\\');
  838. if( -1 == nPos ){
  839. LocalFree(pszUserName);
  840. return false;
  841. }
  842. CString strDomainOrLocalName( strSamComName.Mid(0,nPos) );
  843. WCHAR lpszName1[ MAX_COMPUTERNAME_LENGTH + 1 ];
  844. nSize = MAX_COMPUTERNAME_LENGTH + 1;
  845. GetComputerName( lpszName1, &nSize);
  846. CString strCompName ( (LPCWSTR)lpszName1 );
  847. LocalFree(pszUserName);
  848. return ( strDomainOrLocalName == strCompName );
  849. }
  850. // IsThisUserLoggedIn
  851. //Function determines if the user is the same as the
  852. //passed in DN.
  853. //Parameter is either a DN or a full ADSI path
  854. extern LPWSTR g_lpszLoggedInUser = NULL;
  855. BOOL IsThisUserLoggedIn( LPCTSTR pwszUserDN )
  856. {
  857. DWORD nSize = 0;
  858. BOOL result = FALSE;
  859. if (g_lpszLoggedInUser == NULL) {
  860. //get the size passing null pointer
  861. GetUserNameEx(NameFullyQualifiedDN , NULL, &nSize);
  862. if( nSize == 0 )
  863. return false;
  864. g_lpszLoggedInUser = new WCHAR[ nSize ];
  865. if( g_lpszLoggedInUser == NULL )
  866. return false;
  867. GetUserNameEx(NameFullyQualifiedDN, g_lpszLoggedInUser, &nSize );
  868. }
  869. CString csUserDN = pwszUserDN;
  870. CString csDN;
  871. (void) StripADsIPath(csUserDN, csDN);
  872. if (!_wcsicmp (g_lpszLoggedInUser, csDN)) {
  873. result = TRUE;
  874. }
  875. return result;
  876. }
  877. //+---------------------------------------------------------------------------
  878. //
  879. // Function: SetSecurityInfoMask
  880. //
  881. // Synopsis: Reads the security descriptor from the specied DS object
  882. //
  883. // Arguments: [IN punk] -- IUnknown from IDirectoryObject
  884. // [IN si] -- SecurityInformation
  885. //// History: 25-Dec-2000 -- Hiteshr Created
  886. //----------------------------------------------------------------------------
  887. HRESULT
  888. SetSecurityInfoMask(LPUNKNOWN punk, SECURITY_INFORMATION si)
  889. {
  890. HRESULT hr = E_INVALIDARG;
  891. if (punk)
  892. {
  893. IADsObjectOptions *pOptions;
  894. hr = punk->QueryInterface(IID_IADsObjectOptions, (void**)&pOptions);
  895. if (SUCCEEDED(hr))
  896. {
  897. VARIANT var;
  898. VariantInit(&var);
  899. V_VT(&var) = VT_I4;
  900. V_I4(&var) = si;
  901. hr = pOptions->SetOption(ADS_OPTION_SECURITY_MASK, var);
  902. pOptions->Release();
  903. }
  904. }
  905. return hr;
  906. }
  907. WCHAR const c_szSDProperty[] = L"nTSecurityDescriptor";
  908. //+---------------------------------------------------------------------------
  909. //
  910. // Function: DSReadObjectSecurity
  911. //
  912. // Synopsis: Reads the Dacl from the specied DS object
  913. //
  914. // Arguments: [in pDsObject] -- IDirettoryObject for dsobject
  915. // [psdControl] -- Control Setting for SD
  916. // They can be returned when calling
  917. // DSWriteObjectSecurity
  918. // [OUT ppDacl] -- DACL returned here
  919. //
  920. //
  921. // History 25-Oct-2000 -- hiteshr created
  922. //
  923. // Notes: If Object Doesn't have DACL, function will succeed but *ppDacl will
  924. // be NULL.
  925. // Caller must free *ppDacl, if not NULL, by calling LocalFree
  926. //
  927. //----------------------------------------------------------------------------
  928. HRESULT
  929. DSReadObjectSecurity(IN IDirectoryObject *pDsObject,
  930. OUT SECURITY_DESCRIPTOR_CONTROL * psdControl,
  931. OUT PACL *ppDacl)
  932. {
  933. HRESULT hr = S_OK;
  934. PADS_ATTR_INFO pSDAttributeInfo = NULL;
  935. do // false loop
  936. {
  937. LPWSTR pszSDProperty = (LPWSTR)c_szSDProperty;
  938. DWORD dwAttributesReturned;
  939. PSECURITY_DESCRIPTOR pSD = NULL;
  940. PACL pAcl = NULL;
  941. if(!pDsObject || !ppDacl)
  942. {
  943. ASSERT(FALSE);
  944. hr = E_INVALIDARG;
  945. break;
  946. }
  947. *ppDacl = NULL;
  948. // Set the SECURITY_INFORMATION mask
  949. hr = SetSecurityInfoMask(pDsObject, DACL_SECURITY_INFORMATION);
  950. if(FAILED(hr))
  951. {
  952. break;
  953. }
  954. //
  955. // Read the security descriptor
  956. //
  957. hr = pDsObject->GetObjectAttributes(&pszSDProperty,
  958. 1,
  959. &pSDAttributeInfo,
  960. &dwAttributesReturned);
  961. if (SUCCEEDED(hr) && !pSDAttributeInfo)
  962. hr = E_ACCESSDENIED; // This happens for SACL if no SecurityPrivilege
  963. if(FAILED(hr))
  964. {
  965. break;
  966. }
  967. ASSERT(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->dwADsType);
  968. ASSERT(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->pADsValues->dwType);
  969. pSD = (PSECURITY_DESCRIPTOR)pSDAttributeInfo->pADsValues->SecurityDescriptor.lpValue;
  970. ASSERT(IsValidSecurityDescriptor(pSD));
  971. //
  972. //Get the security descriptor control
  973. //
  974. if(psdControl)
  975. {
  976. DWORD dwRevision;
  977. if(!GetSecurityDescriptorControl(pSD, psdControl, &dwRevision))
  978. {
  979. hr = HRESULT_FROM_WIN32(GetLastError());
  980. break;
  981. }
  982. }
  983. //
  984. //Get pointer to DACL
  985. //
  986. BOOL bDaclPresent, bDaclDefaulted;
  987. if(!GetSecurityDescriptorDacl(pSD,
  988. &bDaclPresent,
  989. &pAcl,
  990. &bDaclDefaulted))
  991. {
  992. hr = HRESULT_FROM_WIN32(GetLastError());
  993. break;
  994. }
  995. if(!bDaclPresent ||
  996. !pAcl)
  997. {
  998. break;
  999. }
  1000. ASSERT(IsValidAcl(pAcl));
  1001. //
  1002. //Make a copy of the DACL
  1003. //
  1004. *ppDacl = (PACL)LocalAlloc(LPTR,pAcl->AclSize);
  1005. if(!*ppDacl)
  1006. {
  1007. hr = E_OUTOFMEMORY;
  1008. break;
  1009. }
  1010. CopyMemory(*ppDacl,pAcl,pAcl->AclSize);
  1011. }while(0);
  1012. if (pSDAttributeInfo)
  1013. FreeADsMem(pSDAttributeInfo);
  1014. return hr;
  1015. }
  1016. const GUID GUID_CONTROL_UserChangePassword =
  1017. { 0xab721a53, 0x1e2f, 0x11d0, { 0x98, 0x19, 0x00, 0xaa, 0x00, 0x40, 0x52, 0x9b}};
  1018. bool CanUserChangePassword(IN IDirectoryObject* pDirObject)
  1019. {
  1020. bool bCanChangePassword = false;
  1021. HRESULT hr = S_OK;
  1022. do // false loop
  1023. {
  1024. //
  1025. // Validate parameters
  1026. //
  1027. if (!pDirObject)
  1028. {
  1029. ASSERT(pDirObject);
  1030. break;
  1031. }
  1032. SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
  1033. CSimpleAclHolder Dacl;
  1034. hr = DSReadObjectSecurity(pDirObject,
  1035. &sdControl,
  1036. &(Dacl.m_pAcl));
  1037. if (FAILED(hr))
  1038. {
  1039. break;
  1040. }
  1041. //
  1042. // Create and Initialize the Self and World SIDs
  1043. //
  1044. CSidHolder selfSid;
  1045. CSidHolder worldSid;
  1046. PSID pSid = NULL;
  1047. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  1048. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  1049. if (!AllocateAndInitializeSid(&NtAuth,
  1050. 1,
  1051. SECURITY_PRINCIPAL_SELF_RID,
  1052. 0, 0, 0, 0, 0, 0, 0,
  1053. &pSid))
  1054. {
  1055. break;
  1056. }
  1057. selfSid.Attach(pSid, false);
  1058. pSid = NULL;
  1059. if (!AllocateAndInitializeSid(&WorldAuth,
  1060. 1,
  1061. SECURITY_WORLD_RID,
  1062. 0, 0, 0, 0, 0, 0, 0,
  1063. &pSid))
  1064. {
  1065. break;
  1066. }
  1067. worldSid.Attach(pSid, false);
  1068. pSid = NULL;
  1069. ULONG ulCount = 0, j = 0;
  1070. PEXPLICIT_ACCESS rgEntries = NULL;
  1071. DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
  1072. if (ERROR_SUCCESS != dwErr)
  1073. {
  1074. break;
  1075. }
  1076. //
  1077. // Are these ACEs already present?
  1078. //
  1079. bool bSelfAllowPresent = false;
  1080. bool bWorldAllowPresent = false;
  1081. bool bSelfDenyPresent = false;
  1082. bool bWorldDenyPresent = false;
  1083. //
  1084. // Loop through looking for the can change password ACE for self and world
  1085. //
  1086. for (j = 0; j < ulCount; j++)
  1087. {
  1088. //
  1089. // Look for deny ACEs
  1090. //
  1091. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  1092. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  1093. {
  1094. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  1095. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  1096. //
  1097. // Look for the user can change password ACE
  1098. //
  1099. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  1100. GUID_CONTROL_UserChangePassword))
  1101. {
  1102. //
  1103. // See if it is for the self SID or the world SID
  1104. //
  1105. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  1106. {
  1107. //
  1108. // Deny self found
  1109. //
  1110. bSelfDenyPresent = true;
  1111. }
  1112. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  1113. {
  1114. //
  1115. // Deny world found
  1116. //
  1117. bWorldDenyPresent = true;
  1118. }
  1119. }
  1120. }
  1121. //
  1122. // Look for allow ACEs
  1123. //
  1124. else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  1125. (rgEntries[j].grfAccessMode == GRANT_ACCESS))
  1126. {
  1127. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  1128. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  1129. //
  1130. // Look for the user can change password ACE
  1131. //
  1132. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  1133. GUID_CONTROL_UserChangePassword))
  1134. {
  1135. //
  1136. // See if it is for the self SID or the world SID
  1137. //
  1138. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  1139. {
  1140. //
  1141. // Allow self found
  1142. //
  1143. bSelfAllowPresent = true;
  1144. }
  1145. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  1146. {
  1147. //
  1148. // Allow world found
  1149. //
  1150. bWorldAllowPresent = true;
  1151. }
  1152. }
  1153. }
  1154. }
  1155. if (bSelfDenyPresent || bWorldDenyPresent)
  1156. {
  1157. //
  1158. // There is an explicit deny so we know that the user cannot change password
  1159. //
  1160. bCanChangePassword = false;
  1161. }
  1162. else if ((!bSelfDenyPresent && !bWorldDenyPresent) &&
  1163. (bSelfAllowPresent || bWorldAllowPresent))
  1164. {
  1165. //
  1166. // There is no explicit deny but there are explicit allows so we know that
  1167. // the user can change password
  1168. //
  1169. bCanChangePassword = true;
  1170. }
  1171. else
  1172. {
  1173. //
  1174. // We are not sure because the explicit entries are not telling us for
  1175. // certain so it all depends on inheritence. Most likely they will
  1176. // be able to change their password unless the admin has changed something
  1177. // higher up or through group membership
  1178. //
  1179. bCanChangePassword = true;
  1180. }
  1181. } while(false);
  1182. return bCanChangePassword;
  1183. }
  1184. /////////////////////////////////////////////////////////////////////////////////
  1185. // CDSNotifyDataObject
  1186. class CDSNotifyDataObject : public IDataObject, public CComObjectRoot
  1187. {
  1188. // ATL Maps
  1189. DECLARE_NOT_AGGREGATABLE(CDSNotifyDataObject)
  1190. BEGIN_COM_MAP(CDSNotifyDataObject)
  1191. COM_INTERFACE_ENTRY(IDataObject)
  1192. END_COM_MAP()
  1193. // Construction/Destruction
  1194. CDSNotifyDataObject()
  1195. {
  1196. m_dwFlags = 0;
  1197. m_dwProviderFlags = 0;
  1198. m_pCLSIDNamespace = NULL;
  1199. }
  1200. ~CDSNotifyDataObject() {}
  1201. // Standard IDataObject methods
  1202. public:
  1203. // Implemented
  1204. STDMETHOD(GetData)(FORMATETC * pformatetcIn, STGMEDIUM * pmedium);
  1205. // Not Implemented
  1206. private:
  1207. STDMETHOD(GetDataHere)(FORMATETC*, STGMEDIUM*) { return E_NOTIMPL; };
  1208. STDMETHOD(EnumFormatEtc)(DWORD, IEnumFORMATETC**) { return E_NOTIMPL; };
  1209. STDMETHOD(SetData)(FORMATETC*, STGMEDIUM*, BOOL) { return E_NOTIMPL; };
  1210. STDMETHOD(QueryGetData)(FORMATETC*) { return E_NOTIMPL; };
  1211. STDMETHOD(GetCanonicalFormatEtc)(FORMATETC*, FORMATETC*) { return E_NOTIMPL; };
  1212. STDMETHOD(DAdvise)(FORMATETC*, DWORD, IAdviseSink*, DWORD*) { return E_NOTIMPL; };
  1213. STDMETHOD(DUnadvise)(DWORD) { return E_NOTIMPL; };
  1214. STDMETHOD(EnumDAdvise)(IEnumSTATDATA**) { return E_NOTIMPL; };
  1215. public:
  1216. // Property Page Clipboard formats
  1217. static CLIPFORMAT m_cfDsObjectNames;
  1218. // initialization
  1219. HRESULT Init(LPCWSTR lpszPath, LPCWSTR lpszClass, BOOL bContainer,
  1220. CDSComponentData* pCD);
  1221. // Implementation
  1222. private:
  1223. CString m_szPath;
  1224. CString m_szClass;
  1225. DWORD m_dwFlags;
  1226. DWORD m_dwProviderFlags;
  1227. const CLSID* m_pCLSIDNamespace;
  1228. };
  1229. CLIPFORMAT CDSNotifyDataObject::m_cfDsObjectNames =
  1230. (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  1231. STDMETHODIMP CDSNotifyDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
  1232. {
  1233. if ((pFormatEtc == NULL) || (pMedium == NULL))
  1234. {
  1235. return E_INVALIDARG;
  1236. }
  1237. if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
  1238. {
  1239. return E_INVALIDARG;
  1240. }
  1241. if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
  1242. {
  1243. return DV_E_TYMED;
  1244. }
  1245. // we support only one clipboard format
  1246. pMedium->tymed = TYMED_HGLOBAL;
  1247. pMedium->pUnkForRelease = NULL;
  1248. if (pFormatEtc->cfFormat != m_cfDsObjectNames)
  1249. {
  1250. return DV_E_FORMATETC;
  1251. }
  1252. // figure out how much storage we need
  1253. int nPathLen = m_szPath.GetLength();
  1254. int nClassLen = m_szClass.GetLength();
  1255. int cbStruct = sizeof(DSOBJECTNAMES); //contains already a DSOBJECT embedded struct
  1256. DWORD cbStorage = (nPathLen + 1 + nClassLen + 1) * sizeof(WCHAR);
  1257. LPDSOBJECTNAMES pDSObj;
  1258. pDSObj = (LPDSOBJECTNAMES)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  1259. cbStruct + cbStorage);
  1260. if (pDSObj == NULL)
  1261. {
  1262. return STG_E_MEDIUMFULL;
  1263. }
  1264. // write the info
  1265. pDSObj->clsidNamespace = *m_pCLSIDNamespace;
  1266. pDSObj->cItems = 1;
  1267. pDSObj->aObjects[0].dwFlags = m_dwFlags;
  1268. pDSObj->aObjects[0].dwProviderFlags = m_dwProviderFlags;
  1269. pDSObj->aObjects[0].offsetName = cbStruct;
  1270. pDSObj->aObjects[0].offsetClass = cbStruct + (nPathLen + 1) * sizeof(WCHAR);
  1271. _tcscpy((LPTSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetName)), (LPCWSTR)m_szPath);
  1272. _tcscpy((LPTSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetClass)), (LPCWSTR)m_szClass);
  1273. pMedium->hGlobal = (HGLOBAL)pDSObj;
  1274. return S_OK;
  1275. }
  1276. HRESULT CDSNotifyDataObject::Init(LPCWSTR lpszPath, LPCWSTR lpszClass, BOOL bContainer,
  1277. CDSComponentData* pCD)
  1278. {
  1279. m_szPath = lpszPath;
  1280. m_szClass = lpszClass;
  1281. switch (pCD->QuerySnapinType())
  1282. {
  1283. case SNAPINTYPE_DS:
  1284. m_pCLSIDNamespace = &CLSID_DSSnapin;
  1285. break;
  1286. case SNAPINTYPE_SITE:
  1287. m_pCLSIDNamespace = &CLSID_SiteSnapin;
  1288. break;
  1289. default:
  1290. m_pCLSIDNamespace = &CLSID_NULL;
  1291. }
  1292. m_dwProviderFlags = (pCD->IsAdvancedView()) ? DSPROVIDER_ADVANCED : 0;
  1293. m_dwFlags = bContainer ? DSOBJECT_ISCONTAINER : 0;
  1294. return S_OK;
  1295. }
  1296. /////////////////////////////////////////////////////////////////////
  1297. // CChangePasswordPrivilegeAction
  1298. static GUID UserChangePasswordGUID =
  1299. { 0xab721a53, 0x1e2f, 0x11d0, { 0x98, 0x19, 0x00, 0xaa, 0x00, 0x40, 0x52, 0x9b}};
  1300. HRESULT CChangePasswordPrivilegeAction::Load(IADs * pIADs)
  1301. {
  1302. // reset state, just in case
  1303. m_bstrObjectLdapPath = (LPCWSTR)NULL;
  1304. m_pDacl = NULL;
  1305. m_SelfSid.Clear();
  1306. m_WorldSid.Clear();
  1307. // get the full LDAP path of the object
  1308. HRESULT hr = pIADs->get_ADsPath(&m_bstrObjectLdapPath);
  1309. ASSERT (SUCCEEDED(hr));
  1310. if (FAILED(hr))
  1311. {
  1312. TRACE(_T("failed on pIADs->get_ADsPath()\n"));
  1313. return hr;
  1314. }
  1315. UnescapePath(m_bstrObjectLdapPath, /*bDN*/ FALSE, m_bstrObjectLdapPath);
  1316. // allocate SIDs
  1317. hr = _SetSids();
  1318. if (FAILED(hr))
  1319. {
  1320. TRACE(_T("failed on _SetSids()\n"));
  1321. return hr;
  1322. }
  1323. // read info
  1324. TRACE(_T("GetNamedSecurityInfo(%s)\n"), m_bstrObjectLdapPath);
  1325. DWORD dwErr = ::GetNamedSecurityInfo(
  1326. IN m_bstrObjectLdapPath,
  1327. IN SE_DS_OBJECT_ALL,
  1328. IN DACL_SECURITY_INFORMATION,
  1329. OUT NULL,
  1330. OUT NULL,
  1331. OUT &(m_pDacl),
  1332. OUT NULL,
  1333. OUT &(m_SDHolder.m_pSD));
  1334. TRACE(L"GetNamedSecurityInfo() returned dwErr = 0x%x\n", dwErr);
  1335. if (dwErr != ERROR_SUCCESS)
  1336. {
  1337. TRACE(L"GetNamedSecurityInfo() failed!\n");
  1338. return HRESULT_FROM_WIN32(dwErr);
  1339. }
  1340. return S_OK;
  1341. }
  1342. HRESULT CChangePasswordPrivilegeAction::Read(BOOL* pbPasswordCannotChange)
  1343. {
  1344. *pbPasswordCannotChange = FALSE;
  1345. // find about the existence of the deny ACEs
  1346. ULONG ulCount, j;
  1347. PEXPLICIT_ACCESS rgEntries;
  1348. ASSERT(m_pDacl);
  1349. DWORD dwErr = GetExplicitEntriesFromAcl(m_pDacl, &ulCount, &rgEntries);
  1350. TRACE(L"GetExplicitEntriesFromAcl() returned dwErr = 0x%x\n", dwErr);
  1351. if (dwErr != ERROR_SUCCESS)
  1352. {
  1353. TRACE(L"GetExplicitEntriesFromAcl() failed!\n");
  1354. return HRESULT_FROM_WIN32(dwErr);
  1355. }
  1356. for (j = 0; j < ulCount; j++)
  1357. {
  1358. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  1359. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  1360. {
  1361. OBJECTS_AND_SID * pObjectsAndSid;
  1362. pObjectsAndSid = (OBJECTS_AND_SID *)rgEntries[j].Trustee.ptstrName;
  1363. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  1364. UserChangePasswordGUID) &&
  1365. (EqualSid(pObjectsAndSid->pSid, m_SelfSid.Get()) ||
  1366. EqualSid(pObjectsAndSid->pSid, m_WorldSid.Get())))
  1367. {
  1368. *pbPasswordCannotChange = TRUE;
  1369. } // if
  1370. } // if
  1371. } // for
  1372. TRACE(L"*pbPasswordCannotChange = %d\n", *pbPasswordCannotChange);
  1373. return S_OK;
  1374. }
  1375. HRESULT CChangePasswordPrivilegeAction::Revoke()
  1376. {
  1377. DWORD dwErr = 0;
  1378. EXPLICIT_ACCESS rgAccessEntry[2] = {0};
  1379. OBJECTS_AND_SID rgObjectsAndSid[2] = {0};
  1380. // initialize the entries (DENY ACE's)
  1381. rgAccessEntry[0].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  1382. rgAccessEntry[0].grfAccessMode = DENY_ACCESS;
  1383. rgAccessEntry[0].grfInheritance = NO_INHERITANCE;
  1384. rgAccessEntry[1].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  1385. rgAccessEntry[1].grfAccessMode = DENY_ACCESS;
  1386. rgAccessEntry[1].grfInheritance = NO_INHERITANCE;
  1387. // build the trustee structs for change password
  1388. BuildTrusteeWithObjectsAndSid(&(rgAccessEntry[0].Trustee),
  1389. &(rgObjectsAndSid[0]),
  1390. &UserChangePasswordGUID,
  1391. NULL, // inherit guid
  1392. m_SelfSid.Get()
  1393. );
  1394. BuildTrusteeWithObjectsAndSid(&(rgAccessEntry[1].Trustee),
  1395. &(rgObjectsAndSid[1]),
  1396. &UserChangePasswordGUID,
  1397. NULL, // inherit guid
  1398. m_WorldSid.Get()
  1399. );
  1400. // add the entries to the ACL
  1401. TRACE(L"calling SetEntriesInAcl()\n");
  1402. CSimpleAclHolder NewDacl;
  1403. dwErr = ::SetEntriesInAcl(2, rgAccessEntry, m_pDacl, &(NewDacl.m_pAcl));
  1404. TRACE(L"SetEntriesInAcl() returned dwErr = 0x%x\n", dwErr);
  1405. if (dwErr != ERROR_SUCCESS)
  1406. {
  1407. TRACE(_T("SetEntriesInAccessList failed!\n"));
  1408. return HRESULT_FROM_WIN32(dwErr);
  1409. }
  1410. // commit the changes
  1411. TRACE(L"calling SetNamedSecurityInfo()\n");
  1412. dwErr = ::SetNamedSecurityInfo(
  1413. IN m_bstrObjectLdapPath,
  1414. IN SE_DS_OBJECT_ALL,
  1415. IN DACL_SECURITY_INFORMATION,
  1416. IN NULL,
  1417. IN NULL,
  1418. IN NewDacl.m_pAcl,
  1419. IN NULL);
  1420. TRACE(L"SetNamedSecurityInfo() returned dwErr = 0x%x\n", dwErr);
  1421. if (dwErr != ERROR_SUCCESS)
  1422. {
  1423. TRACE(_T("SetNamedSecurityInfo() failed!\n"));
  1424. return HRESULT_FROM_WIN32(dwErr);
  1425. }
  1426. return S_OK;
  1427. }
  1428. HRESULT CChangePasswordPrivilegeAction::_SetSids()
  1429. {
  1430. PSID pSidTemp;
  1431. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  1432. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  1433. //
  1434. // build SID's for Self and World.
  1435. //
  1436. if (!AllocateAndInitializeSid(&NtAuth,
  1437. 1,
  1438. SECURITY_PRINCIPAL_SELF_RID,
  1439. 0, 0, 0, 0, 0, 0, 0,
  1440. &pSidTemp))
  1441. {
  1442. TRACE(_T("AllocateAndInitializeSid failed!\n"));
  1443. return HRESULT_FROM_WIN32(GetLastError());
  1444. }
  1445. m_SelfSid.Attach(pSidTemp, FALSE);
  1446. if (!AllocateAndInitializeSid(&WorldAuth,
  1447. 1,
  1448. SECURITY_WORLD_RID,
  1449. 0, 0, 0, 0, 0, 0, 0,
  1450. &pSidTemp))
  1451. {
  1452. TRACE(_T("AllocateAndInitializeSid failed!\n"));
  1453. return HRESULT_FROM_WIN32(GetLastError());
  1454. }
  1455. m_WorldSid.Attach(pSidTemp, FALSE);
  1456. return S_OK;
  1457. }
  1458. /////////////////////////////////////////////////////////////////////
  1459. // CDSNotifyHandlerTransaction
  1460. CDSNotifyHandlerTransaction::CDSNotifyHandlerTransaction(CDSComponentData* pCD)
  1461. {
  1462. m_bStarted = FALSE;
  1463. m_uEvent = 0;
  1464. m_pCD = pCD;
  1465. if (m_pCD != NULL)
  1466. {
  1467. m_pMgr = pCD->GetNotifyHandlerManager();
  1468. ASSERT(m_pMgr != NULL);
  1469. }
  1470. }
  1471. UINT CDSNotifyHandlerTransaction::NeedNotifyCount()
  1472. {
  1473. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1474. return 0; // we have no handler for doing notifications
  1475. if (!m_pMgr->HasHandlers())
  1476. return 0;
  1477. return m_pMgr->NeedNotifyCount(m_uEvent);
  1478. }
  1479. void CDSNotifyHandlerTransaction::SetCheckListBox(CCheckListBox* pCheckListBox)
  1480. {
  1481. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1482. return; // we have no handler for doing notifications
  1483. ASSERT(m_pMgr->HasHandlers());
  1484. m_pMgr->SetCheckListBox(pCheckListBox, m_uEvent);
  1485. }
  1486. void CDSNotifyHandlerTransaction::ReadFromCheckListBox(CCheckListBox* pCheckListBox)
  1487. {
  1488. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1489. return; // we have no handler for doing notifications
  1490. ASSERT(m_pMgr->HasHandlers());
  1491. m_pMgr->ReadFromCheckListBox(pCheckListBox, m_uEvent);
  1492. }
  1493. HRESULT CDSNotifyHandlerTransaction::Begin(LPCWSTR lpszArg1Path,
  1494. LPCWSTR lpszArg1Class,
  1495. BOOL bArg1Cont,
  1496. LPCWSTR lpszArg2Path,
  1497. LPCWSTR lpszArg2Class,
  1498. BOOL bArg2Cont)
  1499. {
  1500. m_bStarted = TRUE;
  1501. ASSERT(m_uEvent != 0);
  1502. m_spArg1 = NULL;
  1503. m_spArg2 = NULL;
  1504. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1505. {
  1506. return S_OK; // we have no handler for doing notifications
  1507. }
  1508. //
  1509. // if needed, do delayed message handler initialization
  1510. //
  1511. m_pMgr->Load(m_pCD->GetBasePathsInfo());
  1512. //
  1513. // avoid building data objects if there are no handlers available
  1514. //
  1515. if (!m_pMgr->HasHandlers())
  1516. {
  1517. return S_OK;
  1518. }
  1519. //
  1520. // build first argument data object
  1521. //
  1522. HRESULT hr = _BuildDataObject(lpszArg1Path, lpszArg1Class, bArg1Cont, &m_spArg1);
  1523. if (FAILED(hr))
  1524. {
  1525. return hr;
  1526. }
  1527. //
  1528. // if needed, build second argument
  1529. //
  1530. if ( (m_uEvent == DSA_NOTIFY_MOV) || (m_uEvent == DSA_NOTIFY_REN) )
  1531. {
  1532. hr = _BuildDataObject(lpszArg2Path, lpszArg2Class, bArg2Cont, &m_spArg2);
  1533. if (FAILED(hr))
  1534. {
  1535. return hr;
  1536. }
  1537. }
  1538. m_pMgr->Begin(m_uEvent, m_spArg1, m_spArg2);
  1539. return S_OK;
  1540. }
  1541. HRESULT CDSNotifyHandlerTransaction::Begin(CDSCookie* pArg1Cookie,
  1542. LPCWSTR lpszArg2Path,
  1543. LPCWSTR lpszArg2Class,
  1544. BOOL bArg2Cont)
  1545. {
  1546. m_bStarted = TRUE;
  1547. ASSERT(m_uEvent != 0);
  1548. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1549. {
  1550. return S_OK; // we have no handler for doing notifications
  1551. }
  1552. //
  1553. // get info from the node and cookie and call the other Begin() function
  1554. //
  1555. CString szPath;
  1556. m_pCD->GetBasePathsInfo()->ComposeADsIPath(szPath, pArg1Cookie->GetPath());
  1557. return Begin(szPath, pArg1Cookie->GetClass(), pArg1Cookie->IsContainerClass(),
  1558. lpszArg2Path, lpszArg2Class, bArg2Cont);
  1559. }
  1560. HRESULT CDSNotifyHandlerTransaction::Begin(IDataObject* pArg1,
  1561. LPCWSTR lpszArg2Path,
  1562. LPCWSTR lpszArg2Class,
  1563. BOOL bArg2Cont)
  1564. {
  1565. m_bStarted = TRUE;
  1566. ASSERT(m_uEvent != 0);
  1567. m_spArg1 = NULL;
  1568. m_spArg2 = NULL;
  1569. HRESULT hr;
  1570. if ((m_pCD == NULL) || (m_pMgr == NULL))
  1571. {
  1572. return S_OK; // we have no handler for doing notifications
  1573. }
  1574. //
  1575. // if needed, do delayed message handler initialization
  1576. //
  1577. m_pMgr->Load(m_pCD->GetBasePathsInfo());
  1578. //
  1579. // avoid building data objects if there are no handlers available
  1580. //
  1581. if (!m_pMgr->HasHandlers())
  1582. {
  1583. return S_OK;
  1584. }
  1585. //
  1586. // get the first argument as is
  1587. //
  1588. m_spArg1 = pArg1;
  1589. //
  1590. // if needed, build second argument
  1591. //
  1592. if ( (m_uEvent != DSA_NOTIFY_DEL) && (m_uEvent != DSA_NOTIFY_PROP) )
  1593. {
  1594. hr = _BuildDataObject(lpszArg2Path, lpszArg2Class, bArg2Cont, &m_spArg2);
  1595. if (FAILED(hr))
  1596. {
  1597. return hr;
  1598. }
  1599. }
  1600. m_pMgr->Begin(m_uEvent, m_spArg1, m_spArg2);
  1601. return S_OK;
  1602. }
  1603. void CDSNotifyHandlerTransaction::Notify(ULONG nItem)
  1604. {
  1605. ASSERT(m_bStarted);
  1606. ASSERT(m_uEvent != 0);
  1607. if ((m_pCD == NULL) || (m_pMgr == NULL) || !m_pMgr->HasHandlers())
  1608. return;
  1609. m_pMgr->Notify(nItem, m_uEvent);
  1610. }
  1611. void CDSNotifyHandlerTransaction::End()
  1612. {
  1613. ASSERT(m_bStarted);
  1614. ASSERT(m_uEvent != 0);
  1615. if ((m_pCD == NULL) || (m_pMgr == NULL) || !m_pMgr->HasHandlers())
  1616. return;
  1617. m_pMgr->End(m_uEvent);
  1618. m_spArg1 = NULL;
  1619. m_spArg2 = NULL;
  1620. m_bStarted = FALSE;
  1621. }
  1622. HRESULT CDSNotifyHandlerTransaction::BuildTransactionDataObject(LPCWSTR lpszArgPath,
  1623. LPCWSTR lpszArgClass,
  1624. BOOL bContainer,
  1625. CDSComponentData* pCD,
  1626. IDataObject** ppArg)
  1627. {
  1628. (*ppArg) = NULL;
  1629. ASSERT((lpszArgPath != NULL) && (lpszArgPath[0] != NULL));
  1630. ASSERT((lpszArgClass != NULL) && (lpszArgClass[0] != NULL));
  1631. //
  1632. // need to build a data object and hang on to it
  1633. //
  1634. CComObject<CDSNotifyDataObject>* pObject;
  1635. CComObject<CDSNotifyDataObject>::CreateInstance(&pObject);
  1636. if (pObject == NULL)
  1637. {
  1638. return E_OUTOFMEMORY;
  1639. }
  1640. HRESULT hr = pObject->FinalConstruct();
  1641. if (FAILED(hr))
  1642. {
  1643. delete pObject;
  1644. return hr;
  1645. }
  1646. hr = pObject->Init(lpszArgPath, lpszArgClass, bContainer, pCD);
  1647. if (FAILED(hr))
  1648. {
  1649. delete pObject;
  1650. return hr;
  1651. }
  1652. hr = pObject->QueryInterface(IID_IDataObject,
  1653. reinterpret_cast<void**>(ppArg));
  1654. if (FAILED(hr))
  1655. {
  1656. //
  1657. // delete object by calling Release()
  1658. //
  1659. (*ppArg)->Release();
  1660. (*ppArg) = NULL;
  1661. return hr;
  1662. }
  1663. return hr;
  1664. }
  1665. HRESULT CDSNotifyHandlerTransaction::_BuildDataObject(LPCWSTR lpszArgPath,
  1666. LPCWSTR lpszArgClass,
  1667. BOOL bContainer,
  1668. IDataObject** ppArg)
  1669. {
  1670. ASSERT(m_uEvent != 0);
  1671. ASSERT(m_pCD != NULL);
  1672. return BuildTransactionDataObject(lpszArgPath, lpszArgClass,bContainer, m_pCD, ppArg);
  1673. }
  1674. /*
  1675. // JonN 6/2/00 99382
  1676. // SITEREPL: Run interference when administrator attempts to
  1677. // delete critical object (NTDS Settings)
  1678. // reports own errors, returns true iff deletion should proceed
  1679. bool CUIOperationHandlerBase::CheckForNTDSDSAInSubtree(
  1680. LPCTSTR lpszX500Path,
  1681. LPCTSTR lpszItemName)
  1682. {
  1683. if (NULL == GetComponentData())
  1684. {
  1685. ASSERT(FALSE);
  1686. return false;
  1687. }
  1688. // set up subtree search
  1689. CDSSearch Search(GetComponentData()->GetClassCache(), GetComponentData());
  1690. CString strRootPath;
  1691. GetComponentData()->GetBasePathsInfo()->ComposeADsIPath(strRootPath, lpszX500Path);
  1692. HRESULT hr = Search.Init(strRootPath);
  1693. // retrieve X500DN path to schema container
  1694. CString strSchemaPath;
  1695. GetComponentData()->GetBasePathsInfo()->GetSchemaPath(strSchemaPath);
  1696. CPathCracker pathCracker;
  1697. pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strSchemaPath),
  1698. ADS_SETTYPE_FULL);
  1699. pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  1700. CComBSTR sbstrSchemaPathX500DN;
  1701. pathCracker.Retrieve(ADS_FORMAT_X500_DN,&sbstrSchemaPathX500DN);
  1702. // filter search
  1703. CString strFilter;
  1704. strFilter = L"(|(objectCategory=CN=NTDS-DSA,";
  1705. strFilter += sbstrSchemaPathX500DN;
  1706. strFilter += L")(&(objectCategory=CN=Computer,";
  1707. strFilter += sbstrSchemaPathX500DN;
  1708. strFilter += L")(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND_W L":=8192)))";
  1709. Search.SetFilterString((LPWSTR)(LPCWSTR)strFilter);
  1710. Search.SetSearchScope(ADS_SCOPE_SUBTREE);
  1711. LPWSTR pAttrs[2] = {L"objectClass",
  1712. L"distinguishedName"};
  1713. Search.SetAttributeList (pAttrs, 2);
  1714. hr = Search.DoQuery();
  1715. if (SUCCEEDED(hr))
  1716. hr = Search.GetNextRow();
  1717. CString strX500PathDC;
  1718. while (SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS)
  1719. {
  1720. ADS_SEARCH_COLUMN Column;
  1721. hr = Search.GetColumn (pAttrs[1], &Column);
  1722. if (FAILED(hr) || Column.dwNumValues < 1) break;
  1723. strX500PathDC = Column.pADsValues[Column.dwNumValues-1].CaseIgnoreString;
  1724. Search.FreeColumn(&Column);
  1725. if (lstrcmpi(lpszX500Path,strX500PathDC))
  1726. break;
  1727. // This is the object being deleted, this check does not apply here.
  1728. // Continue the search.
  1729. hr = Search.GetNextRow();
  1730. }
  1731. if (hr == S_ADS_NOMORE_ROWS)
  1732. return true;
  1733. else if (FAILED(hr))
  1734. return true; // CODEWORK do we want to allow this operation to proceed?
  1735. // retrieve the name of the DC
  1736. CComBSTR sbstrDCName;
  1737. bool fFoundNTDSDSA = FALSE;
  1738. ADS_SEARCH_COLUMN Column;
  1739. hr = Search.GetColumn (pAttrs[0], &Column);
  1740. if (SUCCEEDED(hr) && Column.dwNumValues > 0)
  1741. {
  1742. fFoundNTDSDSA = !lstrcmpi( L"nTDSDSA",
  1743. Column.pADsValues[Column.dwNumValues-1].CaseIgnoreString );
  1744. Search.FreeColumn(&Column);
  1745. pathCracker.Set((LPWSTR)(LPCWSTR)strX500PathDC, ADS_SETTYPE_DN);
  1746. pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1747. pathCracker.GetElement( (fFoundNTDSDSA)?1:0, &sbstrDCName );
  1748. }
  1749. // display an error message
  1750. PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPCTSTR)sbstrDCName};
  1751. (void) ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINS_DC,hr,
  1752. MB_ICONERROR, apv, 2);
  1753. // do not proceed with subtree deletion
  1754. return false;
  1755. }
  1756. */
  1757. //////////////////////////////////////////////////////////////////////////////////
  1758. // CheckForCriticalSystemObjectInSubtree
  1759. //
  1760. // description:
  1761. // This function does a subtree search of the container that is passed in looking
  1762. // for all objects that have isCriticalSystemObject=TRUE or NTDS Settings objects.
  1763. //
  1764. // parameters:
  1765. // lpszX500Path - (IN) the X500 path of the container in which to search for
  1766. // critical system objects
  1767. // lpszItemName - (IN) the displayable name of the container in which to search
  1768. // for critical system objects
  1769. //
  1770. // return value:
  1771. // true - The container does not contain any critical system objects
  1772. // false - The container does contain at least on critical system object
  1773. //////////////////////////////////////////////////////////////////////////////////
  1774. bool CUIOperationHandlerBase::CheckForCriticalSystemObjectInSubtree(
  1775. LPCTSTR lpszX500Path,
  1776. LPCTSTR lpszItemName)
  1777. {
  1778. bool bRet = true;
  1779. if (NULL == GetComponentData())
  1780. {
  1781. ASSERT(FALSE);
  1782. return false;
  1783. }
  1784. CString szCriticalObjPath;
  1785. // set up subtree search
  1786. CDSSearch Search(GetComponentData()->GetClassCache(), GetComponentData());
  1787. HRESULT hr = Search.Init(lpszX500Path);
  1788. if (FAILED(hr))
  1789. {
  1790. ASSERT(FALSE && L"Failed to set the path in the search object");
  1791. return false;
  1792. }
  1793. //
  1794. // retrieve X500DN path to schema container
  1795. //
  1796. CString strSchemaPath;
  1797. GetComponentData()->GetBasePathsInfo()->GetSchemaPath(strSchemaPath);
  1798. CPathCracker pathCracker;
  1799. pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strSchemaPath),
  1800. ADS_SETTYPE_FULL);
  1801. pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  1802. CComBSTR sbstrSchemaPathX500DN;
  1803. pathCracker.Retrieve(ADS_FORMAT_X500_DN,&sbstrSchemaPathX500DN);
  1804. //
  1805. // filter search
  1806. //
  1807. CString strFilter;
  1808. strFilter = L"(|(&(objectClass=*)(isCriticalSystemObject=TRUE))";
  1809. strFilter += L"(|(objectCategory=CN=NTDS-DSA,";
  1810. strFilter += sbstrSchemaPathX500DN;
  1811. //
  1812. // 212232 JonN 10/27/00 Protect interSiteTransport objects
  1813. //
  1814. strFilter += L")(objectCategory=CN=Inter-Site-Transport,";
  1815. strFilter += sbstrSchemaPathX500DN;
  1816. strFilter += L")(&(objectCategory=CN=Computer,";
  1817. strFilter += sbstrSchemaPathX500DN;
  1818. strFilter += L")(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND_W L":=8192))))";
  1819. Search.SetFilterString((LPWSTR)(LPCWSTR)strFilter);
  1820. Search.SetSearchScope(ADS_SCOPE_SUBTREE);
  1821. LPWSTR pAttrs[4] = { L"aDSPath",
  1822. L"objectClass",
  1823. L"distinguishedName",
  1824. L"isCriticalSystemObject"};
  1825. Search.SetAttributeList (pAttrs, 4);
  1826. hr = Search.DoQuery();
  1827. if (SUCCEEDED(hr))
  1828. {
  1829. hr = Search.GetNextRow();
  1830. }
  1831. if (hr == S_ADS_NOMORE_ROWS)
  1832. {
  1833. return true;
  1834. }
  1835. while (SUCCEEDED(hr) && hr != S_ADS_NOMORE_ROWS)
  1836. {
  1837. ADS_SEARCH_COLUMN PathColumn, CriticalColumn;
  1838. hr = Search.GetColumn(pAttrs[3], &CriticalColumn);
  1839. if (SUCCEEDED(hr) && CriticalColumn.pADsValues != NULL && CriticalColumn.pADsValues->Boolean)
  1840. {
  1841. //
  1842. // We found a critical system object so report the error and then return
  1843. //
  1844. hr = Search.GetColumn(pAttrs[0], &PathColumn);
  1845. if (SUCCEEDED(hr) && PathColumn.dwNumValues == 1 && PathColumn.pADsValues != NULL)
  1846. {
  1847. //
  1848. // Get the DN as a Windows style path
  1849. //
  1850. CComBSTR bstrLeaf;
  1851. CPathCracker pathCrackerToo;
  1852. HRESULT hrPathCracker = pathCrackerToo.Set(PathColumn.pADsValues->CaseIgnoreString, ADS_SETTYPE_FULL);
  1853. if (SUCCEEDED(hr))
  1854. {
  1855. hrPathCracker = pathCrackerToo.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
  1856. if (SUCCEEDED(hr))
  1857. {
  1858. hrPathCracker = pathCrackerToo.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1859. if (SUCCEEDED(hr))
  1860. {
  1861. hrPathCracker = pathCrackerToo.Retrieve(ADS_FORMAT_LEAF, &bstrLeaf);
  1862. }
  1863. }
  1864. }
  1865. //
  1866. // display an error message
  1867. //
  1868. if (wcslen(bstrLeaf))
  1869. {
  1870. PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPWSTR)bstrLeaf };
  1871. (void) ReportErrorEx(GetParentHwnd(),IDS_CONTAINS_CRITICALSYSOBJ,S_OK,
  1872. MB_ICONERROR, apv, 2);
  1873. }
  1874. else
  1875. {
  1876. PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)PathColumn.pADsValues->CaseIgnoreString };
  1877. (void) ReportErrorEx(GetParentHwnd(),IDS_CONTAINS_CRITICALSYSOBJ,S_OK,
  1878. MB_ICONERROR, apv, 2);
  1879. }
  1880. Search.FreeColumn(&PathColumn);
  1881. Search.FreeColumn(&CriticalColumn);
  1882. bRet = false;
  1883. break;
  1884. }
  1885. Search.FreeColumn(&CriticalColumn);
  1886. }
  1887. else
  1888. {
  1889. //
  1890. // We found an NTDS Settings object. Report the error and return.
  1891. //
  1892. hr = Search.GetColumn(pAttrs[0], &PathColumn);
  1893. if (SUCCEEDED(hr) && PathColumn.dwNumValues == 1 && PathColumn.pADsValues != NULL)
  1894. {
  1895. CString strX500PathDC = PathColumn.pADsValues[PathColumn.dwNumValues-1].CaseIgnoreString;
  1896. Search.FreeColumn(&PathColumn);
  1897. if (lstrcmpi(lpszX500Path,strX500PathDC))
  1898. {
  1899. //
  1900. // retrieve the name of the DC
  1901. //
  1902. CComBSTR sbstrDCName;
  1903. bool fFoundNTDSDSA = FALSE;
  1904. ADS_SEARCH_COLUMN ClassColumn;
  1905. hr = Search.GetColumn (pAttrs[1], &ClassColumn);
  1906. if (SUCCEEDED(hr) && ClassColumn.dwNumValues > 0)
  1907. {
  1908. fFoundNTDSDSA = !lstrcmpi( L"nTDSDSA",
  1909. ClassColumn.pADsValues[ClassColumn.dwNumValues-1].CaseIgnoreString );
  1910. Search.FreeColumn(&ClassColumn);
  1911. pathCracker.Set((LPWSTR)(LPCWSTR)strX500PathDC, ADS_SETTYPE_DN);
  1912. pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1913. pathCracker.GetElement( (fFoundNTDSDSA)?1:0, &sbstrDCName );
  1914. }
  1915. // display an error message
  1916. PVOID apv[2] = {(PVOID)lpszItemName, (PVOID)(LPCTSTR)sbstrDCName};
  1917. (void) ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINS_DC,hr,
  1918. MB_ICONERROR, apv, 2);
  1919. // do not proceed with subtree deletion
  1920. bRet = false;
  1921. break;
  1922. }
  1923. }
  1924. }
  1925. hr = Search.GetNextRow();
  1926. }
  1927. // do not proceed with subtree deletion
  1928. return bRet;
  1929. }
  1930. /////////////////////////////////////////////////////////////////////
  1931. // CDeleteDCDialog
  1932. class CDeleteDCDialog : public CDialog
  1933. {
  1934. // Construction
  1935. public:
  1936. CDeleteDCDialog(LPCTSTR lpszName, bool fIsComputer);
  1937. // Implementation
  1938. protected:
  1939. // message handlers and MFC overrides
  1940. virtual BOOL OnInitDialog();
  1941. virtual void OnOK();
  1942. DECLARE_MESSAGE_MAP()
  1943. private:
  1944. CString m_strADsPath;
  1945. bool m_fIsComputer;
  1946. };
  1947. BEGIN_MESSAGE_MAP(CDeleteDCDialog, CDialog)
  1948. END_MESSAGE_MAP()
  1949. CDeleteDCDialog::CDeleteDCDialog(LPCTSTR lpszADsPath, bool fIsComputer)
  1950. : CDialog(IDD_DELETE_DC_COMPUTER, NULL)
  1951. , m_strADsPath(lpszADsPath)
  1952. , m_fIsComputer(fIsComputer)
  1953. {
  1954. }
  1955. BOOL CDeleteDCDialog::OnInitDialog()
  1956. {
  1957. // CODEWORK AfxInit?
  1958. CDialog::OnInitDialog();
  1959. CPathCracker pathCracker;
  1960. pathCracker.Set(const_cast<LPWSTR>((LPCWSTR)m_strADsPath), ADS_SETTYPE_FULL);
  1961. pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1962. pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
  1963. CString strDisplay;
  1964. CComBSTR sbstrName;
  1965. if (m_fIsComputer)
  1966. {
  1967. pathCracker.GetElement( 0, &sbstrName );
  1968. strDisplay.FormatMessage(IDS_DELETE_DC_COMPUTERACCOUNT, sbstrName);
  1969. }
  1970. else
  1971. {
  1972. CComBSTR sbstrSite;
  1973. pathCracker.GetElement( 1, &sbstrName );
  1974. pathCracker.GetElement( 3, &sbstrSite );
  1975. CString strTemp;
  1976. (void) GetDlgItemText(IDC_DELETE_DC_MAINTEXT, strTemp);
  1977. strDisplay.FormatMessage( strTemp, sbstrName, sbstrSite );
  1978. }
  1979. (void) SetDlgItemText( IDC_DELETE_DC_MAINTEXT, strDisplay );
  1980. CheckRadioButton( IDC_DELETE_DC_BADREASON1,
  1981. IDC_DELETE_DC_GOODREASON,
  1982. IDC_DELETE_DC_BADREASON1 );
  1983. return TRUE;
  1984. }
  1985. void CDeleteDCDialog::OnOK()
  1986. {
  1987. if (BST_CHECKED == IsDlgButtonChecked(IDC_DELETE_DC_GOODREASON))
  1988. {
  1989. CDialog::OnOK();
  1990. return;
  1991. }
  1992. (void) ReportErrorEx((HWND)*this,
  1993. (BST_CHECKED == IsDlgButtonChecked(IDC_DELETE_DC_BADREASON2))
  1994. ? IDS_DELETE_DC_BADREASON2
  1995. : IDS_DELETE_DC_BADREASON1,
  1996. S_OK,MB_OK, NULL, 0);
  1997. CDialog::OnCancel();
  1998. }
  1999. // JonN 6/15/00 13574
  2000. // Centralizes the checks to make sure this is an OK object to delete
  2001. // returns HRESULT_FROM_WIN32(ERROR_CANCELLED) on cancellation
  2002. // returns refAlreadyDeleted=true iff ObjectDeletionCheck already
  2003. // attempted an alternate deletion method (e.g. DsRemoveDsServer).
  2004. HRESULT CUIOperationHandlerBase::ObjectDeletionCheck(
  2005. LPCTSTR lpszADsPath,
  2006. LPCTSTR lpszName, // shortname to display to user, may be NULL
  2007. LPCTSTR lpszClass,
  2008. bool& fAlternateDeleteMethod )
  2009. {
  2010. fAlternateDeleteMethod = false;
  2011. if (!_wcsicmp(L"user",lpszClass)
  2012. #ifdef INETORGPERSON
  2013. || !_wcsicmp(L"inetOrgPerson", lpszClass)
  2014. #endif
  2015. )
  2016. {
  2017. if (IsThisUserLoggedIn(lpszADsPath))
  2018. {
  2019. CComBSTR sbstrRDN;
  2020. if (NULL == lpszName)
  2021. {
  2022. (void) DSPROP_RetrieveRDN( lpszADsPath, &sbstrRDN );
  2023. lpszName = sbstrRDN;
  2024. }
  2025. PVOID apv[1] = {(PVOID)lpszName};
  2026. if (IDYES != ReportMessageEx (GetParentHwnd(), IDS_12_USER_LOGGED_IN,
  2027. MB_YESNO, apv, 1))
  2028. {
  2029. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2030. }
  2031. }
  2032. return S_OK;
  2033. }
  2034. bool fIsComputer = false;
  2035. if (!_wcsicmp(L"computer",lpszClass))
  2036. {
  2037. //
  2038. // Bind and figure out if the account is a DC
  2039. //
  2040. CComPtr<IADs> spIADs;
  2041. HRESULT hr = ADsOpenObject ((LPWSTR)lpszADsPath,
  2042. NULL, NULL, ADS_SECURE_AUTHENTICATION,
  2043. IID_IADs,
  2044. (void **)&spIADs);
  2045. CComVariant Var;
  2046. if (SUCCEEDED(hr))
  2047. hr = spIADs->Get(L"userAccountControl", &Var);
  2048. if ( FAILED(hr) || !(Var.lVal & UF_SERVER_TRUST_ACCOUNT))
  2049. return S_OK; // cannot be shown to be a DC
  2050. fIsComputer = true;
  2051. }
  2052. else if (!_wcsicmp(L"nTDSDSA",lpszClass))
  2053. {
  2054. //
  2055. // I would like to figure out the domain name so that I could
  2056. // use fCommit==FALSE, but this is a little complicated.
  2057. // Basic code is in proppage GetReplicatedDomainInfo(), but
  2058. // is not exportable in its current form. I will defer this
  2059. // improvement for later.
  2060. //
  2061. }
  2062. else if (!_wcsicmp(L"trustedDomain",lpszClass))
  2063. {
  2064. //
  2065. // Give a strong warning if they are trying to delete a
  2066. // TDO (Trusted Domain Object). This could cause serious
  2067. // problems but we want to allow them to clean up if necessary
  2068. //
  2069. PVOID apv[1] = {(PVOID)lpszName};
  2070. if (IDYES == ReportMessageEx( GetParentHwnd(),
  2071. IDS_WARNING_TDO_DELTEE,
  2072. MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONWARNING,
  2073. apv,
  2074. 1 ) )
  2075. {
  2076. fAlternateDeleteMethod = FALSE;
  2077. return S_OK;
  2078. }
  2079. else
  2080. {
  2081. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2082. }
  2083. }
  2084. else if (!_wcsicmp(L"interSiteTransport",lpszClass))
  2085. {
  2086. //
  2087. // 212232 JonN 10/27/00 Protect interSiteTransport objects
  2088. //
  2089. PVOID apv[1] = {(PVOID)lpszName};
  2090. (void) ReportMessageEx( GetParentHwnd(),
  2091. IDS_1_ERROR_DELETE_CRITOBJ,
  2092. MB_OK | MB_ICONERROR,
  2093. apv,
  2094. 1 );
  2095. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2096. }
  2097. else
  2098. {
  2099. return S_OK; // This is neither a computer nor an nTDSDSA nor a TDO
  2100. }
  2101. // This is either an nTDSDSA object, or a computer object
  2102. // which represents a DC
  2103. CDeleteDCDialog dlg(lpszADsPath,fIsComputer);
  2104. if (IDOK != dlg.DoModal())
  2105. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2106. if (fIsComputer)
  2107. return S_OK;
  2108. // This is an nTDSDSA. Delete using DsRemoveDsServer.
  2109. fAlternateDeleteMethod = true;
  2110. Smart_DsHandle shDS;
  2111. BOOL fLastDcInDomain = FALSE;
  2112. DWORD dwWinError = DsBind(
  2113. m_pComponentData->GetBasePathsInfo()->GetServerName(),
  2114. NULL,
  2115. &shDS );
  2116. if (ERROR_SUCCESS == dwWinError)
  2117. {
  2118. CPathCracker pathCracker;
  2119. pathCracker.Set(const_cast<LPTSTR>(lpszADsPath), ADS_SETTYPE_FULL);
  2120. pathCracker.RemoveLeafElement(); // pass DN to Server object
  2121. CComBSTR sbstrDN;
  2122. pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrDN );
  2123. dwWinError = DsRemoveDsServer( shDS,
  2124. sbstrDN,
  2125. NULL,
  2126. &fLastDcInDomain,
  2127. TRUE );
  2128. }
  2129. return HRESULT_FROM_WIN32(dwWinError);
  2130. }
  2131. ///////////////////////////////////////////////////////////////////////////
  2132. // CSingleDeleteHandlerBase
  2133. /*
  2134. NOTICE: the function will return S_OK on success, S_FALSE if aborted
  2135. by user, some FAILED(hr) otherwise
  2136. */
  2137. HRESULT CSingleDeleteHandlerBase::Delete()
  2138. {
  2139. HRESULT hr = S_OK;
  2140. bool fAlternateDeleteMethod = false;
  2141. // start the transaction
  2142. hr = BeginTransaction();
  2143. ASSERT(SUCCEEDED(hr));
  2144. if (GetTransaction()->NeedNotifyCount() > 0)
  2145. {
  2146. CString szMessage, szAssocData;
  2147. szMessage.LoadString(IDS_CONFIRM_DELETE);
  2148. szAssocData.LoadString(IDS_EXTENS_SINGLE_DEL);
  2149. CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
  2150. dlg.SetStrings(szMessage, szAssocData);
  2151. if (IDNO == dlg.DoModal())
  2152. {
  2153. GetTransaction()->End();
  2154. return S_FALSE;
  2155. }
  2156. }
  2157. else
  2158. {
  2159. // this is just a message box, using ReportErrorEx for consistency of look
  2160. UINT answer = ReportErrorEx(GetParentHwnd(),IDS_CONFIRM_DELETE,S_OK,
  2161. MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2, NULL, 0);
  2162. if (answer == IDNO || answer == IDCANCEL) {
  2163. return S_FALSE; // aborted by user
  2164. }
  2165. }
  2166. CString szName;
  2167. GetItemName(szName);
  2168. hr = ObjectDeletionCheck(
  2169. GetItemPath(),
  2170. szName,
  2171. GetItemClass(),
  2172. fAlternateDeleteMethod );
  2173. if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
  2174. return S_FALSE; // CODEWORK doesn't end transaction?
  2175. else if (FAILED(hr))
  2176. return hr; // CODEWORK doesn't end transaction?
  2177. // try to delete the object
  2178. if (!fAlternateDeleteMethod)
  2179. hr = DeleteObject();
  2180. if (SUCCEEDED(hr))
  2181. {
  2182. // item deleted, notify extensions
  2183. GetTransaction()->Notify(0);
  2184. }
  2185. else
  2186. {
  2187. // error in deleting item, check if it is a special error code
  2188. if (hr == HRESULT_FROM_WIN32(ERROR_DS_CANT_ON_NON_LEAF))
  2189. {
  2190. // ask user to if he/she wants to delete the whole subtree
  2191. PVOID apv[1] = {(LPWSTR)(LPCWSTR)szName};
  2192. UINT answer = ReportErrorEx(GetParentHwnd(),IDS_12_OBJECT_HAS_CHILDREN,hr,
  2193. MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2, apv, 1);
  2194. if (answer == IDYES)
  2195. {
  2196. // JonN 5/22/00 Watch for potential NTDSDSA deletion
  2197. // JeffJon 8/10/00 Watch for potential critical object deletion (isCriticalSystemObject) bug #27377
  2198. //
  2199. if (CheckForCriticalSystemObjectInSubtree(GetItemPath(), szName))
  2200. {
  2201. //
  2202. // try to delete the subtree and continue trying if we reach the 16k limit
  2203. //
  2204. do
  2205. {
  2206. hr = DeleteSubtree();
  2207. } while (HRESULT_CODE(hr) == ERROR_DS_ADMIN_LIMIT_EXCEEDED);
  2208. if (SUCCEEDED(hr))
  2209. {
  2210. GetTransaction()->Notify(0);
  2211. }
  2212. else
  2213. {
  2214. // failed subtree deletion, nothing can be done
  2215. PVOID apvToo[1] = {(LPWSTR)(LPCWSTR)szName};
  2216. ReportErrorEx(GetParentHwnd(), IDS_12_SUBTREE_DELETE_FAILED,hr,
  2217. MB_OK | MB_ICONERROR, apvToo, 1);
  2218. }
  2219. }
  2220. }
  2221. }
  2222. else if (hr == E_ACCESSDENIED)
  2223. {
  2224. PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
  2225. ReportErrorEx(GetParentHwnd(), IDS_12_DELETE_ACCESS_DENIED, hr,
  2226. MB_OK | MB_ICONERROR, apv, 1);
  2227. }
  2228. else if (HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS)
  2229. {
  2230. PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
  2231. ReportErrorEx(GetParentHwnd(),IDS_12_DELETE_PRIMARY_GROUP_FAILED,hr,
  2232. MB_OK | MB_ICONERROR, apv, 1);
  2233. }
  2234. else
  2235. {
  2236. PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)szName};
  2237. ReportErrorEx(GetParentHwnd(),IDS_12_DELETE_FAILED,hr,
  2238. MB_OK | MB_ICONERROR, apv, 1);
  2239. }
  2240. }
  2241. if (SUCCEEDED(hr))
  2242. {
  2243. CStringList szDeletedPathList;
  2244. szDeletedPathList.AddTail(GetItemPath());
  2245. GetComponentData()->InvalidateSavedQueriesContainingObjects(szDeletedPathList);
  2246. }
  2247. return hr;
  2248. GetTransaction()->End();
  2249. return hr;
  2250. }
  2251. ///////////////////////////////////////////////////////////////////////////
  2252. // CMultipleDeleteHandlerBase
  2253. void CMultipleDeleteHandlerBase::Delete()
  2254. {
  2255. HRESULT hr = BeginTransaction();
  2256. ASSERT(SUCCEEDED(hr));
  2257. // ask confirmation to the user
  2258. UINT cCookieTotalCount = GetItemCount();
  2259. CString szFormat;
  2260. szFormat.LoadString(IDS_CONFIRM_MULTI_DELETE);
  2261. CString szMessage;
  2262. szMessage.Format((LPCWSTR)szFormat, cCookieTotalCount);
  2263. if (GetTransaction()->NeedNotifyCount() > 0)
  2264. {
  2265. CString szAssocData;
  2266. szAssocData.LoadString(IDS_EXTENS_MULTIPLE_DEL);
  2267. CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
  2268. dlg.SetStrings(szMessage, szAssocData);
  2269. if (IDNO == dlg.DoModal())
  2270. {
  2271. GetTransaction()->End();
  2272. return;
  2273. }
  2274. }
  2275. else
  2276. {
  2277. PVOID apv[1] = {(LPWSTR)(LPCWSTR)szMessage};
  2278. if (ReportErrorEx(GetParentHwnd(),IDS_STRING,S_OK,
  2279. MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,
  2280. apv, 1)== IDNO)
  2281. {
  2282. return; // user aborted
  2283. }
  2284. }
  2285. CMultipleDeleteProgressDialog dlg(GetParentHwnd(), GetComponentData(), this);
  2286. dlg.DoModal();
  2287. }
  2288. void CMultipleDeleteHandlerBase::OnStart(HWND hwnd)
  2289. {
  2290. SetParentHwnd(hwnd);
  2291. m_confirmationUI.SetWindow(GetParentHwnd());
  2292. }
  2293. HRESULT CMultipleDeleteHandlerBase::OnDeleteStep(IN UINT i,
  2294. OUT BOOL* pbContinue,
  2295. OUT CString& strrefPath,
  2296. OUT CString& strrefClass,
  2297. IN BOOL bSilent)
  2298. {
  2299. ASSERT(i < GetItemCount());
  2300. if (pbContinue == NULL)
  2301. {
  2302. return E_POINTER;
  2303. }
  2304. //
  2305. // Initialize the OUT parameters
  2306. //
  2307. GetItemPath(i, strrefPath);
  2308. strrefClass = GetItemClass(i);
  2309. *pbContinue = TRUE;
  2310. //
  2311. // do the operation
  2312. //
  2313. HRESULT hr = DeleteObject(i);
  2314. if ((SUCCEEDED(hr)))
  2315. {
  2316. // item deleted, notify extensions and end transaction
  2317. GetTransaction()->Notify(i);
  2318. OnItemDeleted(i);
  2319. }
  2320. else
  2321. {
  2322. CString szName;
  2323. GetItemName(i, szName);
  2324. // error in deleting item, check if it is a special error code
  2325. if (hr == HRESULT_FROM_WIN32(ERROR_DS_CANT_ON_NON_LEAF))
  2326. {
  2327. // ask confirmation for deleting subtree
  2328. if (m_confirmationUI.CanDeleteSubtree(hr, szName, pbContinue))
  2329. {
  2330. // JeffJon 8/10/00 Watch for potential deletion of critical system objects
  2331. if ( !CheckForCriticalSystemObjectInSubtree(strrefPath, szName))
  2332. {
  2333. // error already reported
  2334. *pbContinue = FALSE;
  2335. return E_FAIL;
  2336. }
  2337. //
  2338. // Delete the subtree and continue deleting if the 16k limit is reached
  2339. //
  2340. do
  2341. {
  2342. hr = DeleteSubtree(i);
  2343. } while (hr == ERROR_DS_ADMIN_LIMIT_EXCEEDED);
  2344. if (SUCCEEDED(hr))
  2345. {
  2346. // item deleted, notify extensions and end transaction
  2347. GetTransaction()->Notify(i);
  2348. OnItemDeleted(i);
  2349. }
  2350. else
  2351. {
  2352. // we failed subtree deletion
  2353. *pbContinue = m_confirmationUI.ErrorOnSubtreeDeletion(hr, szName);
  2354. }
  2355. }
  2356. else
  2357. {
  2358. //
  2359. // This tells the calling function that we did not delete the object
  2360. // but don't add it to the error reporting
  2361. //
  2362. hr = E_FAIL;
  2363. }
  2364. }
  2365. else
  2366. {
  2367. // we failed deletion
  2368. // JonN 7/20/00 If the HRESULT_FROM_WIN32(ERROR_CANCELLED) case,
  2369. // skip the confirmation UI and just cancel the series.
  2370. if (bSilent)
  2371. {
  2372. *pbContinue = hr != HRESULT_FROM_WIN32(ERROR_CANCELLED);
  2373. }
  2374. else
  2375. {
  2376. *pbContinue = hr != HRESULT_FROM_WIN32(ERROR_CANCELLED) &&
  2377. m_confirmationUI.ErrorOnDeletion(hr, szName);
  2378. }
  2379. } // if (ERROR_DS_CANT_ON_NON_LEAF)
  2380. } // if (delete object)
  2381. return hr;
  2382. }
  2383. //////////////////////////////////////////////////////////////////////////
  2384. // CMoveHandlerBase
  2385. HRESULT CMoveHandlerBase::Move(LPCWSTR lpszDestinationPath)
  2386. {
  2387. // make sure destination data is reset
  2388. m_spDSDestination = NULL;
  2389. m_szDestPath.Empty();
  2390. m_szDestClass.Empty();
  2391. // check nomber of items
  2392. UINT nCount = GetItemCount();
  2393. if (nCount == 0)
  2394. {
  2395. return E_INVALIDARG;
  2396. }
  2397. // get the info about the destination container
  2398. HRESULT hr = _BrowseForDestination(lpszDestinationPath);
  2399. if (FAILED(hr) || (hr == S_FALSE))
  2400. {
  2401. return hr;
  2402. }
  2403. //
  2404. // First check to see if we are trying to move into the same container
  2405. // Using the path of the first object in the multiselect case is OK
  2406. //
  2407. CString szNewPath;
  2408. GetNewPath(0, szNewPath);
  2409. CPathCracker pathCracker;
  2410. hr = pathCracker.Set((PWSTR)(PCWSTR)szNewPath, ADS_SETTYPE_FULL);
  2411. if (SUCCEEDED(hr))
  2412. {
  2413. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  2414. if (SUCCEEDED(hr))
  2415. {
  2416. hr = pathCracker.RemoveLeafElement();
  2417. if (SUCCEEDED(hr))
  2418. {
  2419. CComBSTR sbstrContainerPath;
  2420. hr = pathCracker.Retrieve(ADS_FORMAT_X500, &sbstrContainerPath);
  2421. if (SUCCEEDED(hr))
  2422. {
  2423. if (0 == _wcsicmp(sbstrContainerPath, m_szDestPath))
  2424. {
  2425. //
  2426. // The source and the target container are the same so we
  2427. // don't have to do anything
  2428. //
  2429. return S_OK;
  2430. }
  2431. }
  2432. }
  2433. }
  2434. }
  2435. CStringList szMovedPathList;
  2436. for (UINT nIdx = 0; nIdx < GetItemCount(); nIdx++)
  2437. {
  2438. CString szPath;
  2439. GetItemPath(nIdx, szPath);
  2440. szMovedPathList.AddTail(szPath);
  2441. }
  2442. //
  2443. // Notice that we fall through on a failure trying to crack the source parent path
  2444. // so reset the return value
  2445. //
  2446. hr = S_OK;
  2447. // do the actual move operation
  2448. if (nCount == 1)
  2449. {
  2450. BOOL bContinue = FALSE;
  2451. do
  2452. {
  2453. //
  2454. // Check to be sure we are not trying to drop on itself
  2455. //
  2456. if (m_szDestPath == szNewPath)
  2457. {
  2458. UINT nRet = ReportErrorEx(GetParentHwnd(), IDS_ERR_MSG_NO_MOVE_TO_SELF, S_OK,
  2459. MB_YESNO | MB_ICONERROR, NULL, 0);
  2460. if (nRet == IDYES)
  2461. {
  2462. // get the info about the destination container
  2463. hr = _BrowseForDestination(lpszDestinationPath);
  2464. if (FAILED(hr) || (hr == S_FALSE))
  2465. {
  2466. return hr;
  2467. }
  2468. }
  2469. else
  2470. {
  2471. break;
  2472. }
  2473. }
  2474. else
  2475. {
  2476. bContinue = TRUE;
  2477. }
  2478. } while (!bContinue);
  2479. if (bContinue)
  2480. {
  2481. hr = _MoveSingleSel(szNewPath);
  2482. if (SUCCEEDED(hr))
  2483. {
  2484. GetComponentData()->InvalidateSavedQueriesContainingObjects(szMovedPathList);
  2485. }
  2486. return hr;
  2487. }
  2488. return S_FALSE;
  2489. }
  2490. hr = _MoveMultipleSel();
  2491. return hr;
  2492. }
  2493. BOOL CMoveHandlerBase::_ReportFailure(BOOL bLast, HRESULT hr, LPCWSTR lpszName)
  2494. {
  2495. TRACE(_T("Object Move Failed with hr: %lx\n"), hr);
  2496. PVOID apv[1] = {(LPWSTR)lpszName};
  2497. if (bLast)
  2498. {
  2499. // single selection or last one in multi selection
  2500. ReportErrorEx(GetParentHwnd(),IDS_12_FAILED_TO_MOVE_OBJECT,hr,
  2501. MB_OK | MB_ICONERROR, apv, 1);
  2502. return FALSE; // do not continue
  2503. }
  2504. return (ReportErrorEx(GetParentHwnd(),IDS_12_MULTI_FAILED_TO_MOVE_OBJECT,hr,
  2505. MB_YESNO, apv, 1) == IDYES);
  2506. }
  2507. HRESULT CMoveHandlerBase::_MoveSingleSel(PCWSTR pszNewPath)
  2508. {
  2509. if (!_BeginTransactionAndConfirmOperation())
  2510. return S_FALSE;
  2511. CComPtr<IDispatch> spDSTargetIDispatch;
  2512. HRESULT hr = m_spDSDestination->MoveHere(const_cast<PWSTR>(pszNewPath),
  2513. NULL,
  2514. &spDSTargetIDispatch);
  2515. if (FAILED(hr))
  2516. {
  2517. CString szName;
  2518. GetName(0, szName);
  2519. _ReportFailure(TRUE, hr, szName);
  2520. }
  2521. else
  2522. {
  2523. // all went fine, notify extensions
  2524. GetTransaction()->Notify(0);
  2525. // give a chance to update UI (e.g. cookies)
  2526. CComPtr<IADs> spIADsTarget;
  2527. hr = spDSTargetIDispatch->QueryInterface (IID_IADs,
  2528. (void **)&spIADsTarget);
  2529. hr = OnItemMoved(0, spIADsTarget);
  2530. }
  2531. GetTransaction()->End();
  2532. return hr;
  2533. }
  2534. HRESULT CMoveHandlerBase::_MoveMultipleSel()
  2535. {
  2536. if (!_BeginTransactionAndConfirmOperation())
  2537. return S_FALSE;
  2538. CMultipleMoveProgressDialog dlg(GetParentHwnd(), GetComponentData(), this);
  2539. dlg.DoModal();
  2540. return S_OK;
  2541. }
  2542. HRESULT CMoveHandlerBase::_OnMoveStep(IN UINT i,
  2543. OUT BOOL* pbCanContinue,
  2544. OUT CString& strrefPath,
  2545. OUT CString& strrefClass)
  2546. {
  2547. ASSERT(m_spDSDestination != NULL);
  2548. if (pbCanContinue == NULL)
  2549. {
  2550. return E_POINTER;
  2551. }
  2552. UINT nCount = GetItemCount();
  2553. //
  2554. // Initialize out parameters
  2555. //
  2556. GetItemPath(i, strrefPath);
  2557. strrefClass = GetItemClass(i);
  2558. *pbCanContinue = TRUE;
  2559. if (strrefPath == m_szDestPath)
  2560. {
  2561. return S_OK;
  2562. }
  2563. // try to execute the move
  2564. CString szNewPath;
  2565. CComPtr<IDispatch> spDSTargetIDispatch;
  2566. GetNewPath(i, szNewPath);
  2567. HRESULT hr = m_spDSDestination->MoveHere((LPWSTR)(LPCWSTR)szNewPath,
  2568. NULL,
  2569. &spDSTargetIDispatch);
  2570. if (FAILED(hr))
  2571. {
  2572. CString szName;
  2573. GetName(i, szName);
  2574. if (nCount == 1)
  2575. {
  2576. *pbCanContinue = _ReportFailure((i == nCount-1), hr, szName);
  2577. }
  2578. }
  2579. else
  2580. {
  2581. // all went fine, notify extensions
  2582. GetTransaction()->Notify(i);
  2583. // give a chance to update UI (e.g. cookies)
  2584. CComPtr<IADs> spIADsTarget;
  2585. hr = spDSTargetIDispatch->QueryInterface (IID_IADs,
  2586. (void **)&spIADsTarget);
  2587. hr = OnItemMoved(i, spIADsTarget);
  2588. }
  2589. return hr;
  2590. }
  2591. void BuildBrowseQueryString(LPCWSTR lpszSchemaNamingContext, BOOL bAdvancedView,
  2592. CString& szQueryString)
  2593. {
  2594. // allowed list of container classes
  2595. static LPCWSTR lpszAllowedContainers[] =
  2596. {
  2597. L"Organizational-Unit",
  2598. L"Builtin-Domain",
  2599. L"Lost-And-Found",
  2600. L"container",
  2601. NULL // end of table
  2602. };
  2603. CString sz = L"(|";
  2604. for (int k=0; lpszAllowedContainers[k] != NULL; k++)
  2605. {
  2606. sz += L"(ObjectCategory=CN=";
  2607. sz += lpszAllowedContainers[k];
  2608. sz += L",";
  2609. sz += lpszSchemaNamingContext;
  2610. sz += L")";
  2611. }
  2612. sz += L")";
  2613. if( bAdvancedView )
  2614. {
  2615. szQueryString = sz;
  2616. }
  2617. else
  2618. {
  2619. szQueryString.Format(L"(&%s(!showInAdvancedViewOnly=TRUE))", (LPCWSTR)sz);
  2620. }
  2621. }
  2622. int BrowseCallback(HWND, UINT uMsg, LPARAM lParam, LPARAM lpData)
  2623. {
  2624. switch (uMsg)
  2625. {
  2626. case DSBM_HELP:
  2627. {
  2628. TRACE(L"Browse Callback: msg is DSBM_HELP.\n");
  2629. LPHELPINFO pHelp = (LPHELPINFO) lParam;
  2630. TRACE(_T("CtrlId = %d, ContextId = 0x%x\n"),
  2631. pHelp->iCtrlId, pHelp->dwContextId);
  2632. if (pHelp->iCtrlId != DSBID_CONTAINERLIST) {
  2633. return 0; // not handled
  2634. }
  2635. ::WinHelp((HWND)pHelp->hItemHandle,
  2636. DSADMIN_CONTEXT_HELP_FILE,
  2637. HELP_WM_HELP,
  2638. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_BROWSE_CONTAINER);
  2639. }
  2640. break;
  2641. case DSBM_GETBROWSEDATA:
  2642. {
  2643. // called to change the LDAP query string
  2644. TRACE(L"Browse Callback: msg is DSBM_HELP.\n");
  2645. CDSComponentData* pCD = (CDSComponentData*)lpData;
  2646. if ( (pCD == NULL) || (SNAPINTYPE_SITE == pCD->QuerySnapinType()) ||
  2647. pCD->ExpandComputers() )
  2648. {
  2649. // if we are in site and repl we do not change the default
  2650. // string because we do not have too many objects
  2651. // if we expand computer, users, etc, we do not really
  2652. // have a choice but allow (objectClass=*)
  2653. TRACE(L"Browse Callback: DSBM_HELP, leave the default setting.\n");
  2654. return 0; // just leave the default setting
  2655. }
  2656. DSBROWSEDATA* pdbd = (DSBROWSEDATA*)lParam;
  2657. TRACE(L"pdbd->pszFilter = %s\n", pdbd->pszFilter);
  2658. TRACE(L"pdbd->cchFilter = %d\n", pdbd->cchFilter);
  2659. TRACE(L"pdbd->pszNameAttribute = %s\n", pdbd->pszNameAttribute);
  2660. TRACE(L"pdbd->cchNameAttribute = %d\n", pdbd->cchNameAttribute);
  2661. // need to change query string
  2662. CString szQueryString;
  2663. FilterElementStruct* pFilterElementStructDrillDown = &g_filterelementDsAdminHardcoded;
  2664. BuildFilterElementString(szQueryString, pFilterElementStructDrillDown,
  2665. pCD->GetBasePathsInfo()->GetSchemaNamingContext());
  2666. szQueryString = L"(|" + szQueryString + L")";
  2667. /*
  2668. BuildBrowseQueryString(pCD->GetBasePathsInfo()->GetSchemaNamingContext(),
  2669. pCD->IsAdvancedView(), szQueryString);
  2670. */
  2671. // copy over the new filter
  2672. int nNewFilterLen = szQueryString.GetLength();
  2673. ASSERT(nNewFilterLen < pdbd->cchFilter);
  2674. if (nNewFilterLen >= pdbd->cchFilter)
  2675. {
  2676. return 0; // failed, filter string too long
  2677. }
  2678. wcscpy(pdbd->pszFilter, szQueryString);
  2679. pdbd->cchFilter = nNewFilterLen;
  2680. TRACE(L"New pdbd->pszFilter = %s\n", pdbd->pszFilter);
  2681. TRACE(L"New pdbd->cchFilter = %d\n", pdbd->cchFilter);
  2682. }
  2683. break;
  2684. } // switch
  2685. return 1; // handled
  2686. }
  2687. HRESULT CMoveHandlerBase::_BrowseForDestination(LPCWSTR lpszDestinationPath)
  2688. {
  2689. m_spDSDestination = NULL;
  2690. m_szDestPath.Empty();
  2691. m_szDestClass.Empty();
  2692. // check if we have to expand computers in the Browse for container UI
  2693. CDSComponentData* pCD = GetComponentData();
  2694. BOOL bExpandComputers = FALSE;
  2695. if (pCD != NULL)
  2696. {
  2697. bExpandComputers = pCD->ExpandComputers();
  2698. }
  2699. // determine if we have to show the Browse for container dialog
  2700. CString strTargetContainer;
  2701. if (lpszDestinationPath != NULL)
  2702. {
  2703. // we have the target container already, no need to
  2704. // bring up UI
  2705. strTargetContainer = lpszDestinationPath;
  2706. }
  2707. else
  2708. {
  2709. // no container, need Browse dialog
  2710. CString strClassOfMovedItem;
  2711. GetClassOfMovedItem(strClassOfMovedItem);
  2712. if (0 == strClassOfMovedItem.CompareNoCase(L"server"))
  2713. {
  2714. HICON hIcon = NULL;
  2715. if (pCD != NULL)
  2716. {
  2717. MyBasePathsInfo* pBasePathsInfo = pCD->GetBasePathsInfo();
  2718. ASSERT(pBasePathsInfo != NULL);
  2719. hIcon = pBasePathsInfo->GetIcon(const_cast<LPTSTR>(gsz_site),
  2720. DSGIF_ISNORMAL | DSGIF_GETDEFAULTICON | DSGIF_DEFAULTISCONTAINER,
  2721. 16, 16);
  2722. }
  2723. CMoveServerDialog dlg( m_lpszBrowseRootPath, hIcon, CWnd::FromHandle(GetParentHwnd()) );
  2724. INT_PTR result = dlg.DoModal();
  2725. if (IDCANCEL == result)
  2726. return S_FALSE;
  2727. strTargetContainer = dlg.m_strTargetContainer;
  2728. }
  2729. else
  2730. {
  2731. PWSTR pszPath = new WCHAR[INTERNET_MAX_URL_LENGTH * 4];
  2732. if (!pszPath)
  2733. {
  2734. return E_OUTOFMEMORY;
  2735. }
  2736. pszPath[0] = TEXT('\0');
  2737. CString strTitle;
  2738. strTitle.LoadString (IDS_MOVE_TITLE);
  2739. DSBROWSEINFO dsbi;
  2740. ::ZeroMemory( &dsbi, sizeof(dsbi) );
  2741. CString str;
  2742. str.LoadString(IDS_MOVE_TARGET);
  2743. dsbi.hwndOwner = GetParentHwnd();
  2744. // CODEWORK: Get DsBrowseForContainer to take const strings
  2745. dsbi.cbStruct = sizeof (DSBROWSEINFO);
  2746. dsbi.pszCaption = (LPWSTR)((LPCWSTR)strTitle); // this is actually the caption
  2747. dsbi.pszTitle = (LPWSTR)((LPCWSTR)str);
  2748. dsbi.pszRoot = (LPWSTR)m_lpszBrowseRootPath;
  2749. dsbi.pszPath = pszPath;
  2750. dsbi.cchPath = INTERNET_MAX_URL_LENGTH * 4;
  2751. dsbi.dwFlags = DSBI_RETURN_FORMAT |
  2752. DSBI_EXPANDONOPEN;
  2753. if( pCD && pCD->IsAdvancedView() )
  2754. {
  2755. dsbi.dwFlags |= DSBI_INCLUDEHIDDEN;
  2756. }
  2757. if (bExpandComputers)
  2758. {
  2759. dsbi.dwFlags |= DSBI_IGNORETREATASLEAF;
  2760. }
  2761. dsbi.pfnCallback = BrowseCallback;
  2762. dsbi.lParam = (LPARAM)pCD;
  2763. dsbi.dwReturnFormat = ADS_FORMAT_X500;
  2764. DWORD result = DsBrowseForContainer( &dsbi ); // returns -1, 0, IDOK or IDCANCEL
  2765. if (result != IDOK)
  2766. {
  2767. if (pszPath)
  2768. {
  2769. delete[] pszPath;
  2770. pszPath = 0;
  2771. }
  2772. return S_FALSE; // canceled by user
  2773. }
  2774. strTargetContainer = dsbi.pszPath;
  2775. if (pszPath)
  2776. {
  2777. delete[] pszPath;
  2778. pszPath = 0;
  2779. }
  2780. } // class is not server
  2781. } // have target container
  2782. if ( strTargetContainer.IsEmpty() ) // ADSI doesn't like this
  2783. {
  2784. ASSERT(FALSE);
  2785. return S_FALSE;
  2786. }
  2787. // try to open the target container
  2788. CComPtr<IADsContainer> spDSDestination;
  2789. HRESULT hr = DSAdminOpenObject(strTargetContainer,
  2790. IID_IADsContainer,
  2791. (void **)&spDSDestination,
  2792. FALSE /*bServer*/);
  2793. if (FAILED(hr))
  2794. {
  2795. CPathCracker pathCracker;
  2796. pathCracker.Set(const_cast<LPTSTR>((LPCTSTR)strTargetContainer),
  2797. ADS_SETTYPE_FULL);
  2798. pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  2799. CComBSTR DestName;
  2800. pathCracker.GetElement( 0, &DestName );
  2801. PVOID apv[1] = {(BSTR)DestName};
  2802. ReportErrorEx(GetParentHwnd(),IDS_12_CONTAINER_NOT_FOUND,hr,
  2803. MB_OK | MB_ICONERROR, apv, 1);
  2804. return hr;
  2805. }
  2806. // need the class of the destination container
  2807. CComPtr<IADs> spIADs;
  2808. hr = spDSDestination->QueryInterface(IID_IADs, (void**)&spIADs);
  2809. if (FAILED(hr))
  2810. {
  2811. ASSERT(FALSE);
  2812. return hr;
  2813. }
  2814. CComBSTR bstrClass;
  2815. hr = spIADs->get_Class(&bstrClass);
  2816. if (FAILED(hr))
  2817. {
  2818. ASSERT(FALSE);
  2819. return hr;
  2820. }
  2821. // all went well, copy the output parameters
  2822. m_spDSDestination = spDSDestination;
  2823. m_szDestPath = strTargetContainer;
  2824. m_szDestClass = bstrClass;
  2825. m_bDestContainer = TRUE; // we do a move, it must be one
  2826. return hr;
  2827. }
  2828. BOOL CMoveHandlerBase::_BeginTransactionAndConfirmOperation()
  2829. {
  2830. // start the transaction
  2831. HRESULT hr = BeginTransaction();
  2832. ASSERT(SUCCEEDED(hr));
  2833. // if needed, confirm
  2834. if (GetTransaction()->NeedNotifyCount() > 0)
  2835. {
  2836. CString szMessage;
  2837. CString szAssocData;
  2838. UINT nCount = GetItemCount();
  2839. if (nCount == 1)
  2840. {
  2841. szMessage.LoadString(IDS_CONFIRM_MOVE);
  2842. szAssocData.LoadString(IDS_EXTENS_SINGLE_MOVE);
  2843. }
  2844. else
  2845. {
  2846. CString szMessageFormat;
  2847. szMessageFormat.LoadString(IDS_CONFIRM_MULTIPLE_MOVE);
  2848. szMessage.Format(szMessageFormat, nCount);
  2849. szAssocData.LoadString(IDS_EXTENS_MULTIPLE_MOVE);
  2850. }
  2851. CConfirmOperationDialog dlg(GetParentHwnd(), GetTransaction());
  2852. dlg.SetStrings(szMessage, szAssocData);
  2853. if (IDNO == dlg.DoModal())
  2854. {
  2855. GetTransaction()->End();
  2856. return FALSE;
  2857. }
  2858. }
  2859. return TRUE;
  2860. }
  2861. ///////////////////////////////////////////////////////////////
  2862. // IsHomogenousDSSelection
  2863. //
  2864. // pDataObject must support the DSAdmin internal clipboard format
  2865. //
  2866. // if the return value is true, szClassName will be the name of
  2867. // the class of the homogenous selection
  2868. //
  2869. BOOL IsHomogenousDSSelection(LPDATAOBJECT pDataObject, CString& szClassName)
  2870. {
  2871. BOOL bHomogenous = TRUE;
  2872. szClassName = L"";
  2873. if (pDataObject == NULL)
  2874. {
  2875. ASSERT(FALSE);
  2876. return FALSE;
  2877. }
  2878. CInternalFormatCracker ifc;
  2879. HRESULT hr = ifc.Extract(pDataObject);
  2880. if (FAILED(hr))
  2881. {
  2882. ASSERT(FALSE);
  2883. return FALSE;
  2884. }
  2885. CUINode* pUINode = ifc.GetCookie();
  2886. ASSERT(pUINode != NULL);
  2887. CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(pUINode);
  2888. if (pDSUINode == NULL)
  2889. {
  2890. ASSERT(FALSE);
  2891. return FALSE;
  2892. }
  2893. CDSCookie* pCookie = GetDSCookieFromUINode(pDSUINode);
  2894. if (pCookie == NULL)
  2895. {
  2896. ASSERT(FALSE);
  2897. return FALSE;
  2898. }
  2899. szClassName = pCookie->GetClass();
  2900. ASSERT(!szClassName.IsEmpty());
  2901. for (UINT idx = 1; idx < ifc.GetCookieCount(); idx++)
  2902. {
  2903. CUINode* pSelectedUINode = ifc.GetCookie(idx);
  2904. ASSERT(pSelectedUINode);
  2905. CDSUINode* pSelectedDSUINode = dynamic_cast<CDSUINode*>(pSelectedUINode);
  2906. if (!pSelectedDSUINode)
  2907. {
  2908. bHomogenous = FALSE;
  2909. break;
  2910. }
  2911. CDSCookie* pSelectedCookie = GetDSCookieFromUINode(pSelectedDSUINode);
  2912. if (!pSelectedCookie)
  2913. {
  2914. ASSERT(FALSE);
  2915. bHomogenous = FALSE;
  2916. break;
  2917. }
  2918. if (wcscmp(szClassName, pSelectedCookie->GetClass()) != 0)
  2919. {
  2920. bHomogenous = FALSE;
  2921. break;
  2922. }
  2923. }
  2924. if (!bHomogenous)
  2925. {
  2926. szClassName = L"";
  2927. }
  2928. return bHomogenous;
  2929. }
  2930. //////////////////////////////////////////////////////////////////////////////////
  2931. //////////////////////////////////////////////////////////////////////////////////
  2932. //////////////////////////////////////////////////////////////////////////////////
  2933. // Temporary Tab Collector stuff
  2934. //
  2935. //+----------------------------------------------------------------------------
  2936. //
  2937. // Function: AddPageProc
  2938. //
  2939. // Synopsis: The IShellPropSheetExt->AddPages callback.
  2940. //
  2941. //-----------------------------------------------------------------------------
  2942. BOOL CALLBACK AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall)
  2943. {
  2944. TRACE(_T("xx.%03x> AddPageProc()\n"), GetCurrentThreadId());
  2945. HRESULT hr;
  2946. hr = ((LPPROPERTYSHEETCALLBACK)pCall)->AddPage(hPage);
  2947. return hr == S_OK;
  2948. }
  2949. HRESULT GetDisplaySpecifierProperty(PCWSTR pszClassName,
  2950. PCWSTR pszDisplayProperty,
  2951. MyBasePathsInfo* pBasePathsInfo,
  2952. CStringList& strListRef,
  2953. bool bEnglishOnly)
  2954. {
  2955. HRESULT hr = S_OK;
  2956. //
  2957. // Validate parameters
  2958. // Note : pszClassName can be NULL and will retrieve the default-Display values
  2959. //
  2960. if (pszDisplayProperty == NULL ||
  2961. pBasePathsInfo == NULL)
  2962. {
  2963. ASSERT(FALSE);
  2964. return E_POINTER;
  2965. }
  2966. CComPtr<IADs> spIADs;
  2967. if (!bEnglishOnly)
  2968. {
  2969. hr = pBasePathsInfo->GetDisplaySpecifier(pszClassName, IID_IADs, (PVOID*)&spIADs);
  2970. }
  2971. else
  2972. {
  2973. //
  2974. // Build the path to the English display specifier container
  2975. //
  2976. CString szConfigDN = pBasePathsInfo->GetConfigNamingContext();
  2977. CString szEnglishDisplaySpecifierDN = L"CN=409,CN=DisplaySpecifiers," + szConfigDN;
  2978. CString szDisplayObjectDN = L"CN=" + CString(pszClassName) + L"-Display," + szEnglishDisplaySpecifierDN;
  2979. CString szDisplayObjectPath;
  2980. pBasePathsInfo->ComposeADsIPath(szDisplayObjectPath, szDisplayObjectDN);
  2981. //
  2982. // Open the object and get the property
  2983. //
  2984. hr = DSAdminOpenObject(szDisplayObjectPath,
  2985. IID_IADs,
  2986. (void**)&spIADs,
  2987. true);
  2988. }
  2989. if (SUCCEEDED(hr) && !!spIADs)
  2990. {
  2991. CComVariant var;
  2992. hr = spIADs->Get((LPWSTR)pszDisplayProperty, &var);
  2993. if (SUCCEEDED(hr))
  2994. {
  2995. hr = HrVariantToStringList(var, strListRef);
  2996. }
  2997. }
  2998. return hr;
  2999. }
  3000. HRESULT TabCollect_GetDisplayGUIDs(LPCWSTR lpszClassName,
  3001. LPCWSTR lpszDisplayProperty,
  3002. MyBasePathsInfo* pBasePathsInfo,
  3003. UINT* pnCount,
  3004. GUID** ppGuids)
  3005. {
  3006. HRESULT hr = S_OK;
  3007. //
  3008. // This should bind to the display specifiers, get the specified property and
  3009. // sort the guids by ordered pairs and return the guids
  3010. //
  3011. if (pBasePathsInfo == NULL)
  3012. {
  3013. *pnCount = 0;
  3014. *ppGuids = NULL;
  3015. return E_FAIL;
  3016. }
  3017. CStringList szPropertyList;
  3018. hr = GetDisplaySpecifierProperty(lpszClassName, lpszDisplayProperty, pBasePathsInfo, szPropertyList);
  3019. if (FAILED(hr))
  3020. {
  3021. *pnCount = 0;
  3022. *ppGuids = NULL;
  3023. return hr;
  3024. }
  3025. if (szPropertyList.GetCount() < 1)
  3026. {
  3027. //
  3028. // Couldn't find anything for the class, try to find something in the default-Display
  3029. //
  3030. hr = GetDisplaySpecifierProperty(L"default", lpszDisplayProperty, pBasePathsInfo, szPropertyList);
  3031. if (FAILED(hr))
  3032. {
  3033. //
  3034. // If still nothing is found revert to the English display specifiers
  3035. //
  3036. hr = GetDisplaySpecifierProperty(lpszClassName, lpszDisplayProperty, pBasePathsInfo, szPropertyList, true);
  3037. if (FAILED(hr))
  3038. {
  3039. *pnCount = 0;
  3040. *ppGuids = NULL;
  3041. return hr;
  3042. }
  3043. if (szPropertyList.GetCount() < 1)
  3044. {
  3045. //
  3046. // Now try the English default
  3047. //
  3048. hr = GetDisplaySpecifierProperty(L"default", lpszDisplayProperty, pBasePathsInfo, szPropertyList, true);
  3049. if (FAILED(hr))
  3050. {
  3051. *pnCount = 0;
  3052. *ppGuids = NULL;
  3053. return hr;
  3054. }
  3055. }
  3056. }
  3057. }
  3058. *pnCount = static_cast<UINT>(szPropertyList.GetCount());
  3059. *ppGuids = new GUID[*pnCount];
  3060. if (*ppGuids == NULL)
  3061. {
  3062. *pnCount = 0;
  3063. *ppGuids = NULL;
  3064. return hr;
  3065. }
  3066. int* pnIndex = new int[*pnCount];
  3067. if (pnIndex == NULL)
  3068. {
  3069. *pnCount = 0;
  3070. *ppGuids = NULL;
  3071. return hr;
  3072. }
  3073. CString szIndex;
  3074. CString szGUID;
  3075. UINT itr = 0;
  3076. POSITION pos = szPropertyList.GetHeadPosition();
  3077. while (pos != NULL)
  3078. {
  3079. CString szItem = szPropertyList.GetNext(pos);
  3080. int nComma = szItem.Find(L",");
  3081. if (nComma == -1)
  3082. continue;
  3083. szIndex = szItem.Left(nComma);
  3084. int nIndex = _wtoi((LPCWSTR)szIndex);
  3085. if (nIndex <= 0)
  3086. continue; // allow from 1 up
  3087. // strip leading and traling blanks
  3088. szGUID = szItem.Mid(nComma+1);
  3089. szGUID.TrimLeft();
  3090. szGUID.TrimRight();
  3091. GUID guid;
  3092. hr = ::CLSIDFromString((LPWSTR)(LPCWSTR)szGUID, &guid);
  3093. if (SUCCEEDED(hr))
  3094. {
  3095. (*ppGuids)[itr] = guid;
  3096. pnIndex[itr] = nIndex;
  3097. itr++;
  3098. }
  3099. }
  3100. //
  3101. // Must sort the page list
  3102. //
  3103. while (TRUE)
  3104. {
  3105. BOOL bSwapped = FALSE;
  3106. for (UINT k=1; k < *pnCount; k++)
  3107. {
  3108. if (pnIndex[k] < pnIndex[k-1])
  3109. {
  3110. // swap
  3111. int nTemp = pnIndex[k];
  3112. pnIndex[k] = pnIndex[k-1];
  3113. pnIndex[k-1] = nTemp;
  3114. GUID temp = *ppGuids[k];
  3115. *ppGuids[k] = *ppGuids[k-1];
  3116. *ppGuids[k-1] = temp;
  3117. bSwapped = TRUE;
  3118. }
  3119. }
  3120. if (!bSwapped)
  3121. {
  3122. break;
  3123. }
  3124. }
  3125. //
  3126. // Cleanup the index array
  3127. //
  3128. if (pnIndex != NULL)
  3129. {
  3130. delete[] pnIndex;
  3131. }
  3132. return hr;
  3133. }
  3134. //**********************************************************************
  3135. // Test code to improve the search process on cookies
  3136. BOOL _SearchTreeForCookie(IN CUINode* pContainerNode, // current container where to start the search
  3137. IN CPathCracker* pPathCracker, // path cracker with the tokenized search path
  3138. IN long nCurrentToken, // current token in the path cracker
  3139. IN BOOL bSearchSubcontainers, // flag to search subcontainers
  3140. OUT CUINode** ppUINode // returned node
  3141. )
  3142. {
  3143. ASSERT(pContainerNode != NULL);
  3144. ASSERT(pContainerNode->IsContainer());
  3145. long nPathElements = 0;
  3146. pPathCracker->GetNumElements(&nPathElements);
  3147. if (nCurrentToken >= nPathElements)
  3148. {
  3149. // ran out of tokens to compare
  3150. return FALSE;
  3151. }
  3152. CComBSTR bstrCurrentToken;
  3153. pPathCracker->GetElement(nCurrentToken, &bstrCurrentToken);
  3154. // decide which list to look into
  3155. CUINodeList* pNodeList = NULL;
  3156. if (bSearchSubcontainers)
  3157. pNodeList = pContainerNode->GetFolderInfo()->GetContainerList();
  3158. else
  3159. pNodeList = pContainerNode->GetFolderInfo()->GetLeafList();
  3160. CPathCracker pathCrackerCurr;
  3161. for (POSITION pos = pNodeList->GetHeadPosition(); pos != NULL; )
  3162. {
  3163. CUINode* pCurrentNode = pNodeList->GetNext(pos);
  3164. if (!IS_CLASS(*pCurrentNode, CDSUINode))
  3165. {
  3166. // not a node with a cookie, just skip
  3167. continue;
  3168. }
  3169. // get the cookie from the node
  3170. CDSCookie* pCurrentCookie = GetDSCookieFromUINode(pCurrentNode);
  3171. // build the naming token (leaf element of the path), e.g. "CN=foo"
  3172. CComBSTR bstrCurrentNamingToken;
  3173. pathCrackerCurr.Set((BSTR)pCurrentCookie->GetPath(), ADS_SETTYPE_DN);
  3174. pathCrackerCurr.GetElement(0, &bstrCurrentNamingToken);
  3175. // compare the current naming token with the current search token
  3176. TRACE(L"comparing bstrCurrentToken = %s, bstrCurrentNamingToken = %s\n",
  3177. bstrCurrentToken, bstrCurrentNamingToken);
  3178. if (_wcsicmp(bstrCurrentToken, bstrCurrentNamingToken) == 0)
  3179. {
  3180. // the token matches, need to see if we are at the end of the
  3181. // list of tokens
  3182. if (nCurrentToken == 0)
  3183. {
  3184. *ppUINode = pCurrentNode;
  3185. return TRUE; // got it!!!
  3186. }
  3187. else
  3188. {
  3189. // we match, but we have to go one level deeper
  3190. BOOL bFound = FALSE;
  3191. if (nCurrentToken == 1)
  3192. {
  3193. // try on leaf nodes, we are at the last level
  3194. bFound = _SearchTreeForCookie(pCurrentNode, pPathCracker, nCurrentToken-1, FALSE, ppUINode);
  3195. }
  3196. if (bFound)
  3197. return TRUE;
  3198. // try on subcontainers
  3199. return _SearchTreeForCookie(pCurrentNode, pPathCracker, nCurrentToken-1, TRUE, ppUINode);
  3200. }
  3201. }
  3202. // if no match, we keep scanning at this level
  3203. } // for
  3204. return FALSE; // not found
  3205. }
  3206. BOOL FindCookieInSubtree(IN CUINode* pContainerNode,
  3207. IN LPCWSTR lpszCookieDN,
  3208. IN SnapinType snapinType,
  3209. OUT CUINode** ppUINode)
  3210. {
  3211. *ppUINode = NULL;
  3212. if (!pContainerNode->IsContainer())
  3213. {
  3214. // not the right type of node
  3215. return FALSE;
  3216. }
  3217. LPCWSTR lpszStartingContainerPath = NULL;
  3218. long nAdjustLevel = 0;
  3219. if (IS_CLASS(*pContainerNode, CDSUINode) )
  3220. {
  3221. lpszStartingContainerPath = dynamic_cast<CDSUINode*>(pContainerNode)->GetCookie()->GetPath();
  3222. nAdjustLevel = 1;
  3223. }
  3224. else if (IS_CLASS(*pContainerNode, CRootNode) )
  3225. {
  3226. lpszStartingContainerPath = dynamic_cast<CRootNode*>(pContainerNode)->GetPath();
  3227. if (snapinType == SNAPINTYPE_SITE)
  3228. {
  3229. nAdjustLevel = 1;
  3230. }
  3231. }
  3232. if (lpszStartingContainerPath == NULL)
  3233. {
  3234. // bad node type
  3235. return FALSE;
  3236. }
  3237. // instantiate a path cracker for the DN we are in search of
  3238. CPathCracker pathCrackerDN;
  3239. HRESULT hr = pathCrackerDN.Set((BSTR)lpszCookieDN, ADS_SETTYPE_DN);
  3240. long nPathElementsDN = 0;
  3241. hr = pathCrackerDN.GetNumElements(&nPathElementsDN);
  3242. if ( FAILED(hr) || (nPathElementsDN <= 0) )
  3243. {
  3244. // bad path
  3245. ASSERT(FALSE);
  3246. return FALSE;
  3247. }
  3248. // instantiate a path cracker for the container node
  3249. CPathCracker pathCrackerStartingContainer;
  3250. pathCrackerStartingContainer.Set((BSTR)lpszStartingContainerPath, ADS_SETTYPE_DN);
  3251. long nPathElementsStartingContainer = 0;
  3252. pathCrackerStartingContainer.GetNumElements(&nPathElementsStartingContainer);
  3253. if ( FAILED(hr) || (nPathElementsStartingContainer <= 0) )
  3254. {
  3255. // bad path
  3256. ASSERT(FALSE);
  3257. return FALSE;
  3258. }
  3259. // compute the level where we start the search from
  3260. long nStartToken = nPathElementsDN - nPathElementsStartingContainer - nAdjustLevel;
  3261. if ( nStartToken < 0)
  3262. {
  3263. return FALSE;
  3264. }
  3265. if (( nStartToken == 0) && (nAdjustLevel == 1) && snapinType != SNAPINTYPE_SITE)
  3266. {
  3267. return FALSE;
  3268. }
  3269. return _SearchTreeForCookie(pContainerNode, &pathCrackerDN, nStartToken /*current token*/, TRUE, ppUINode);
  3270. }
  3271. //**********************************************************************
  3272. ///////////////////////////////////////////////////////////////////////////
  3273. // CMultiselectMoveDataObject
  3274. // helper function for CDSEvent::_Paste()
  3275. // to create a data object containing the successfully pasted items
  3276. HRESULT CMultiselectMoveDataObject::BuildPastedDataObject(
  3277. IN CObjectNamesFormatCracker* pObjectNamesFormatPaste,
  3278. IN CMultiselectMoveHandler* pMoveHandler,
  3279. IN CDSComponentData* pCD,
  3280. OUT IDataObject** ppSuccesfullyPastedDataObject)
  3281. {
  3282. // verify input parameters
  3283. if (ppSuccesfullyPastedDataObject == NULL)
  3284. {
  3285. return E_INVALIDARG;
  3286. }
  3287. *ppSuccesfullyPastedDataObject = NULL;
  3288. if ((pObjectNamesFormatPaste == NULL) || (pMoveHandler == NULL) )
  3289. {
  3290. return E_INVALIDARG;
  3291. }
  3292. //
  3293. // need to build a data object and hang on to it
  3294. //
  3295. CComObject<CMultiselectMoveDataObject>* pObject;
  3296. CComObject<CMultiselectMoveDataObject>::CreateInstance(&pObject);
  3297. if (pObject == NULL)
  3298. {
  3299. return E_OUTOFMEMORY;
  3300. }
  3301. HRESULT hr = pObject->FinalConstruct();
  3302. if (FAILED(hr))
  3303. {
  3304. delete pObject;
  3305. return hr;
  3306. }
  3307. hr = pObject->Init(pObjectNamesFormatPaste, pMoveHandler, pCD);
  3308. if (FAILED(hr))
  3309. {
  3310. delete pObject;
  3311. return hr;
  3312. }
  3313. hr = pObject->QueryInterface(IID_IDataObject,
  3314. reinterpret_cast<void**>(ppSuccesfullyPastedDataObject));
  3315. if (FAILED(hr))
  3316. {
  3317. //
  3318. // delete object by calling Release()
  3319. //
  3320. (*ppSuccesfullyPastedDataObject)->Release();
  3321. (*ppSuccesfullyPastedDataObject) = NULL;
  3322. }
  3323. return hr;
  3324. }
  3325. CLIPFORMAT CMultiselectMoveDataObject::m_cfDsObjectNames =
  3326. (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  3327. STDMETHODIMP CMultiselectMoveDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
  3328. {
  3329. if ((pFormatEtc == NULL) || (pMedium == NULL))
  3330. {
  3331. return E_INVALIDARG;
  3332. }
  3333. if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
  3334. {
  3335. return E_INVALIDARG;
  3336. }
  3337. if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
  3338. {
  3339. return DV_E_TYMED;
  3340. }
  3341. // we support only one clipboard format
  3342. pMedium->tymed = TYMED_HGLOBAL;
  3343. pMedium->pUnkForRelease = NULL;
  3344. if (pFormatEtc->cfFormat != m_cfDsObjectNames)
  3345. {
  3346. return DV_E_FORMATETC;
  3347. }
  3348. // make a deep copy of the cached data
  3349. pMedium->hGlobal = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  3350. m_nDSObjCachedBytes);
  3351. if (pMedium->hGlobal == NULL)
  3352. {
  3353. return E_OUTOFMEMORY;
  3354. }
  3355. memcpy(pMedium->hGlobal, m_pDSObjCached, m_nDSObjCachedBytes);
  3356. pMedium->tymed = TYMED_HGLOBAL;
  3357. pMedium->pUnkForRelease = NULL;
  3358. return S_OK;
  3359. }
  3360. HRESULT CMultiselectMoveDataObject::Init(
  3361. IN CObjectNamesFormatCracker* pObjectNamesFormatPaste,
  3362. IN CMultiselectMoveHandler* pMoveHandler,
  3363. IN CDSComponentData* pCD)
  3364. {
  3365. _Clear();
  3366. // figure out how much storage we need
  3367. //
  3368. // this loop is to calc how much storage we need.
  3369. //
  3370. UINT nPasteCount = pObjectNamesFormatPaste->GetCount();
  3371. UINT nSuccessfulPasteCount = 0;
  3372. size_t cbStorage = 0;
  3373. for (UINT i=0; i<nPasteCount; i++)
  3374. {
  3375. if (pMoveHandler->WasItemMoved(i))
  3376. {
  3377. nSuccessfulPasteCount++;
  3378. cbStorage += (wcslen(pObjectNamesFormatPaste->GetClass(i)) + 1 +
  3379. wcslen(pObjectNamesFormatPaste->GetName(i)) + 1) * sizeof(WCHAR);
  3380. }
  3381. }
  3382. if (nSuccessfulPasteCount == 0)
  3383. {
  3384. // no items were successfully pasted
  3385. return E_INVALIDARG;
  3386. }
  3387. // NOTICE: contains already a DSOBJECT embedded struct, so we subtract 1
  3388. DWORD cbStruct = sizeof(DSOBJECTNAMES) +
  3389. ((nSuccessfulPasteCount - 1) * sizeof(DSOBJECT));
  3390. //
  3391. // Allocate the needed storage
  3392. //
  3393. m_pDSObjCached = (LPDSOBJECTNAMES)malloc(cbStruct + cbStorage);
  3394. if (m_pDSObjCached == NULL)
  3395. {
  3396. return E_OUTOFMEMORY;
  3397. }
  3398. m_nDSObjCachedBytes = static_cast<ULONG>(cbStruct + cbStorage);
  3399. switch (pCD->QuerySnapinType())
  3400. {
  3401. case SNAPINTYPE_DS:
  3402. m_pDSObjCached->clsidNamespace = CLSID_DSSnapin;
  3403. break;
  3404. case SNAPINTYPE_SITE:
  3405. m_pDSObjCached->clsidNamespace = CLSID_SiteSnapin;
  3406. break;
  3407. default:
  3408. m_pDSObjCached->clsidNamespace = CLSID_NULL;
  3409. }
  3410. m_pDSObjCached->cItems = nSuccessfulPasteCount;
  3411. DWORD NextOffset = cbStruct;
  3412. UINT index = 0;
  3413. for (i=0; i<nPasteCount; i++)
  3414. {
  3415. if (pMoveHandler->WasItemMoved(i))
  3416. {
  3417. //
  3418. // Set the data from the node and node data
  3419. //
  3420. size_t nNameLen = wcslen(pObjectNamesFormatPaste->GetName(i));
  3421. size_t nClassLen = wcslen(pObjectNamesFormatPaste->GetClass(i));
  3422. ASSERT((nNameLen > 0) && (nClassLen > 0));
  3423. m_pDSObjCached->aObjects[index].dwFlags = pObjectNamesFormatPaste->IsContainer(i) ? DSOBJECT_ISCONTAINER : 0;
  3424. m_pDSObjCached->aObjects[index].dwProviderFlags = (pCD->IsAdvancedView()) ?
  3425. DSPROVIDER_ADVANCED : 0;
  3426. m_pDSObjCached->aObjects[index].offsetName = NextOffset;
  3427. m_pDSObjCached->aObjects[index].offsetClass = static_cast<ULONG>(NextOffset +
  3428. (nNameLen + 1) * sizeof(WCHAR));
  3429. _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetName(i));
  3430. NextOffset += static_cast<ULONG>((nNameLen + 1) * sizeof(WCHAR));
  3431. _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), pObjectNamesFormatPaste->GetClass(i));
  3432. NextOffset += static_cast<ULONG>((nClassLen + 1) * sizeof(WCHAR));
  3433. index++;
  3434. } // if
  3435. } // for
  3436. return S_OK;
  3437. }
  3438. void EscapeFilterElement(PCWSTR pszElement, CString& refszEscapedElement)
  3439. {
  3440. // do LDAP escaping (as per RFC 2254)
  3441. for (const WCHAR* pChar = pszElement; (*pChar) != NULL; pChar++)
  3442. {
  3443. switch (*pChar)
  3444. {
  3445. case L'*':
  3446. refszEscapedElement += L"\\2a";
  3447. break;
  3448. case L'(':
  3449. refszEscapedElement += L"\\28";
  3450. break;
  3451. case L')':
  3452. refszEscapedElement += L"\\29";
  3453. break;
  3454. case L'\\':
  3455. refszEscapedElement += L"\\5c";
  3456. break;
  3457. default:
  3458. refszEscapedElement += (*pChar);
  3459. } // switch
  3460. } // for
  3461. }