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.

2663 lines
74 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Windows NT Directory Service Property Pages
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1999
  7. //
  8. // File: proputil.cxx
  9. //
  10. // Contents: CDsPropPagesHost IUnknown and ClassFactory, CDsPropDataObj.
  11. //
  12. // History: 21-March-97 EricB created
  13. //
  14. //-----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "proppage.h"
  17. #include "notify.h"
  18. #include <propcfg.h> // DS Admin definition of PPROPSHEETCFG
  19. #include <ntdsapip.h>
  20. #include <shlobjp.h> // SHCreatePropertyBag()
  21. //
  22. // This CLSID for the Domain Tree snapin is copied from cdomain.cpp.
  23. //
  24. const CLSID CLSID_DomainAdmin = { /* ebc53a38-a23f-11d0-b09b-00c04fd8dca6 */
  25. 0xebc53a38,
  26. 0xa23f,
  27. 0x11d0,
  28. {0xb0, 0x9b, 0x00, 0xc0, 0x4f, 0xd8, 0xdc, 0xa6}
  29. };
  30. //BOOL CALLBACK AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall);
  31. HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN,
  32. IDataObject * pParentObj, HWND hwndParent, HWND hNotifyObj, BOOL fReadOnly);
  33. HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN,
  34. IDataObject * pParentObj, HWND hwndParent, BOOL fReadOnly);
  35. //+----------------------------------------------------------------------------
  36. //
  37. // CDsPropPagesHost IUnknown methods
  38. //
  39. //-----------------------------------------------------------------------------
  40. //+----------------------------------------------------------------------------
  41. //
  42. // Member: CDsPropPagesHost::IUnknown::QueryInterface
  43. //
  44. // Synopsis: Returns requested interface pointer
  45. //
  46. //-----------------------------------------------------------------------------
  47. STDMETHODIMP
  48. CDsPropPagesHost::QueryInterface(REFIID riid, void ** ppvObject)
  49. {
  50. TRACE2(CDsPropPagesHost,QueryInterface);
  51. if (IID_IUnknown == riid)
  52. {
  53. *ppvObject = (IUnknown *)(LPSHELLEXTINIT)this;
  54. }
  55. else if (IID_IShellExtInit == riid)
  56. {
  57. *ppvObject = (LPSHELLEXTINIT)this;
  58. }
  59. else if (IID_IShellPropSheetExt == riid)
  60. {
  61. *ppvObject = (LPSHELLPROPSHEETEXT)this;
  62. }
  63. else
  64. {
  65. *ppvObject = NULL;
  66. return E_NOINTERFACE;
  67. }
  68. AddRef();
  69. return S_OK;
  70. }
  71. //+----------------------------------------------------------------------------
  72. //
  73. // Member: CDsPropPagesHost::IUnknown::AddRef
  74. //
  75. // Synopsis: increments reference count
  76. //
  77. // Returns: the reference count
  78. //
  79. //-----------------------------------------------------------------------------
  80. STDMETHODIMP_(ULONG)
  81. CDsPropPagesHost::AddRef(void)
  82. {
  83. dspDebugOut((DEB_USER2, "CDsPropPagesHost::AddRef refcount going in %d\n", m_uRefs));
  84. return InterlockedIncrement((long *)&m_uRefs);
  85. }
  86. //+----------------------------------------------------------------------------
  87. //
  88. // Member: CDsPropPagesHost::IUnknown::Release
  89. //
  90. // Synopsis: Decrements the object's reference count and frees it when
  91. // no longer referenced.
  92. //
  93. // Returns: zero if the reference count is zero or non-zero otherwise
  94. //
  95. //-----------------------------------------------------------------------------
  96. STDMETHODIMP_(ULONG)
  97. CDsPropPagesHost::Release(void)
  98. {
  99. dspDebugOut((DEB_USER2, "CDsPropPagesHost::Release ref count going in %d\n", m_uRefs));
  100. unsigned long uTmp;
  101. if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
  102. {
  103. delete this;
  104. }
  105. return uTmp;
  106. }
  107. //+----------------------------------------------------------------------------
  108. //
  109. // CDsPropPagesHostCF - class factory for the CDsPropPagesHost object
  110. //
  111. //-----------------------------------------------------------------------------
  112. //+----------------------------------------------------------------------------
  113. //
  114. // Member: CDsPropPagesHostCF::Create
  115. //
  116. // Synopsis: creates a new class factory object
  117. //
  118. //-----------------------------------------------------------------------------
  119. IClassFactory *
  120. CDsPropPagesHostCF::Create(PDSCLASSPAGES pDsPP)
  121. {
  122. return new CDsPropPagesHostCF(pDsPP);
  123. }
  124. //+----------------------------------------------------------------------------
  125. //
  126. // Member: CDsPropPagesHostCF::CDsPropPagesHostCF
  127. //
  128. // Synopsis: ctor
  129. //
  130. //-----------------------------------------------------------------------------
  131. CDsPropPagesHostCF::CDsPropPagesHostCF(PDSCLASSPAGES pDsPP) :
  132. m_pDsPP(pDsPP),
  133. m_uRefs(1)
  134. {
  135. TRACE2(CDsPropPagesHostCF,CDsPropPagesHostCF);
  136. #ifdef _DEBUG
  137. strcpy(szClass, "CDsPropPagesHostCF");
  138. #endif
  139. }
  140. //+----------------------------------------------------------------------------
  141. //
  142. // Member: CDsPropPagesHostCF::~CDsPropPagesHostCF
  143. //
  144. // Synopsis: dtor
  145. //
  146. //-----------------------------------------------------------------------------
  147. CDsPropPagesHostCF::~CDsPropPagesHostCF(void)
  148. {
  149. TRACE2(CDsPropPagesHostCF,~CDsPropPagesHostCF);
  150. }
  151. //+----------------------------------------------------------------------------
  152. //
  153. // Member: CDsPropPagesHostCF::IUnknown::QueryInterface
  154. //
  155. // Synopsis: Returns requested interface pointer
  156. //
  157. //-----------------------------------------------------------------------------
  158. STDMETHODIMP
  159. CDsPropPagesHostCF::QueryInterface(REFIID riid, void ** ppvObject)
  160. {
  161. dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::QueryInterface\n"));
  162. if (IID_IUnknown == riid)
  163. {
  164. *ppvObject = (IUnknown *)this;
  165. }
  166. else if (IsEqualIID(IID_IClassFactory, riid))
  167. {
  168. *ppvObject = (IClassFactory *)this;
  169. }
  170. else
  171. {
  172. *ppvObject = NULL;
  173. return E_NOINTERFACE;
  174. }
  175. AddRef();
  176. return S_OK;
  177. }
  178. //+----------------------------------------------------------------------------
  179. //
  180. // Member: CDsPropPagesHostCF::IUnknown::AddRef
  181. //
  182. // Synopsis: increments reference count
  183. //
  184. // Returns: the new reference count
  185. //
  186. //-----------------------------------------------------------------------------
  187. STDMETHODIMP_(ULONG)
  188. CDsPropPagesHostCF::AddRef(void)
  189. {
  190. dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::AddRef refcount going in %d\n", m_uRefs));
  191. return InterlockedIncrement((long *)&m_uRefs);
  192. }
  193. //+----------------------------------------------------------------------------
  194. //
  195. // Member: CDsPropPagesHostCF::IUnknown::Release
  196. //
  197. // Synopsis: decrement the refcount
  198. //
  199. // Returns: the new reference count
  200. //
  201. //-----------------------------------------------------------------------------
  202. STDMETHODIMP_(ULONG)
  203. CDsPropPagesHostCF::Release(void)
  204. {
  205. dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::Release ref count going in %d\n", m_uRefs));
  206. unsigned long uTmp;
  207. if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
  208. {
  209. delete this;
  210. }
  211. return uTmp;
  212. }
  213. //+----------------------------------------------------------------------------
  214. //
  215. // Member: CDsPropPagesHostCF::IClassFactory::CreateInstance
  216. //
  217. // Synopsis: create an incore instance of the proppage host class object
  218. //
  219. // Arguments: [pUnkOuter] - aggregator
  220. // [riid] - requested interface
  221. // [ppvObject] - receptor for itf ptr
  222. //
  223. // Returns: HRESULTS
  224. //
  225. //-----------------------------------------------------------------------------
  226. STDMETHODIMP
  227. CDsPropPagesHostCF::CreateInstance(IUnknown*, REFIID riid,
  228. void **ppvObject)
  229. {
  230. TRACE2(CDsPropPagesHostCF,CreateInstance);
  231. HRESULT hr = S_OK;
  232. *ppvObject = NULL;
  233. CDsPropPagesHost * pPropPage = new CDsPropPagesHost(m_pDsPP);
  234. if (pPropPage == NULL)
  235. {
  236. return E_OUTOFMEMORY;
  237. }
  238. hr = pPropPage->QueryInterface(riid, ppvObject);
  239. if (FAILED(hr))
  240. {
  241. ERR_OUT("CDsPropPagesHostCF::CreateInstance, pPropPage->QueryInterface", hr);
  242. pPropPage->Release();
  243. return hr;
  244. }
  245. //
  246. // We got a refcount of one when launched, and the above QI increments it
  247. // to 2, so call release to take it back to 1.
  248. //
  249. pPropPage->Release();
  250. return hr;
  251. }
  252. //+----------------------------------------------------------------------------
  253. //
  254. // Member: CDsPropPagesHostCF::IClassFactory::LockServer
  255. //
  256. // Synopsis: Called with fLock set to TRUE to indicate that the server
  257. // should continue to run even if none of its objects are active
  258. //
  259. // Arguments: [fLock] - increment/decrement the instance count
  260. //
  261. // Returns: HRESULTS
  262. //
  263. //-----------------------------------------------------------------------------
  264. STDMETHODIMP
  265. CDsPropPagesHostCF::LockServer(BOOL fLock)
  266. {
  267. CDll::LockServer(fLock);
  268. return S_OK;
  269. }
  270. //+----------------------------------------------------------------------------
  271. //
  272. // Function: AllocWStr
  273. //
  274. // Synopsis: Creates a copy of the passed in string. Allocates memory for
  275. // the returned string using new. Callers must free the string
  276. // memory when done using delete.
  277. //
  278. // Returns: FALSE for out of memory failures.
  279. //
  280. //-----------------------------------------------------------------------------
  281. BOOL AllocWStr(PWSTR pwzStrIn, PWSTR * ppwzNewStr)
  282. {
  283. if (pwzStrIn == NULL)
  284. {
  285. *ppwzNewStr = NULL;
  286. return TRUE;
  287. }
  288. *ppwzNewStr = new WCHAR[wcslen(pwzStrIn) + 1];
  289. if (NULL == *ppwzNewStr)
  290. {
  291. return FALSE;
  292. }
  293. wcscpy(*ppwzNewStr, pwzStrIn);
  294. return TRUE;
  295. }
  296. //+----------------------------------------------------------------------------
  297. //
  298. // Function: AllocTStr
  299. //
  300. // Synopsis: Creates a copy of the passed in string. Allocates memory for
  301. // the returned string using new. Callers must free the string
  302. // memory when done using delete.
  303. //
  304. // Returns: FALSE for out of memory failures.
  305. //
  306. //-----------------------------------------------------------------------------
  307. BOOL AllocTStr(PTSTR ptzStrIn, PTSTR * pptzNewStr)
  308. {
  309. *pptzNewStr = new TCHAR[_tcslen(ptzStrIn) + 1];
  310. if (NULL == *pptzNewStr)
  311. {
  312. return FALSE;
  313. }
  314. _tcscpy(*pptzNewStr, ptzStrIn);
  315. return TRUE;
  316. }
  317. //+----------------------------------------------------------------------------
  318. //
  319. // Function: UnicodeToTchar
  320. //
  321. // Synopsis: Converts a Unicode string to a TCHAR string. Allocates memory
  322. // for the returned string using new. Callers must free the
  323. // string memory when done using delete.
  324. //
  325. // Returns: FALSE for out of memory failures.
  326. //
  327. //-----------------------------------------------------------------------------
  328. BOOL UnicodeToTchar(LPWSTR pwszIn, LPTSTR * pptszOut)
  329. {
  330. size_t len;
  331. #ifdef UNICODE
  332. len = wcslen(pwszIn);
  333. #else
  334. len = WideCharToMultiByte(CP_ACP, 0, pwszIn, -1, NULL, 0, NULL, NULL);
  335. #endif
  336. *pptszOut = new TCHAR[len + 1];
  337. CHECK_NULL(*pptszOut, return FALSE);
  338. #ifdef UNICODE
  339. wcscpy(*pptszOut, pwszIn);
  340. #else
  341. if (WideCharToMultiByte(CP_ACP, 0, pwszIn, -1,
  342. *pptszOut, len, NULL, NULL) == 0)
  343. {
  344. delete *pptszOut;
  345. return FALSE;
  346. }
  347. #endif
  348. return TRUE;
  349. }
  350. //+----------------------------------------------------------------------------
  351. //
  352. // Function: TcharToUnicode
  353. //
  354. // Synopsis: Converts a TCHAR string to a Unicode string. Allocates memory
  355. // for the returned string using new. Callers must free the
  356. // string memory when done using delete.
  357. //
  358. // Returns: FALSE for out of memory failures.
  359. //
  360. //-----------------------------------------------------------------------------
  361. BOOL TcharToUnicode(LPTSTR ptszIn, LPWSTR * ppwszOut)
  362. {
  363. size_t len;
  364. #ifdef UNICODE
  365. len = wcslen(ptszIn);
  366. #else
  367. len = MultiByteToWideChar(CP_ACP, 0, ptszIn, -1, NULL, 0);
  368. #endif
  369. *ppwszOut = new WCHAR[len + 1];
  370. CHECK_NULL(*ppwszOut, return FALSE);
  371. #ifdef UNICODE
  372. wcscpy(*ppwszOut, ptszIn);
  373. #else
  374. if (MultiByteToWideChar(CP_ACP, 0, ptszIn, -1, *ppwszOut, len) == 0)
  375. {
  376. delete *ppwszOut;
  377. return FALSE;
  378. }
  379. #endif
  380. return TRUE;
  381. }
  382. #ifdef _PROVIDE_CFSTR_SHELLIDLIST_FORMAT
  383. // FROM DIZ (dsuiext\query.cpp)
  384. //#if DELEGATE
  385. //WCHAR c_szDsMagicPath[] = L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}";
  386. //#else
  387. WCHAR c_szDsMagicPath[] = L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork\\::{fe1290f0-cfbd-11cf-a330-00aa00c16e65}";
  388. //#endif
  389. #if !DOWNLEVEL_SHELL
  390. /*-----------------------------------------------------------------------------
  391. / BindToPath
  392. / ----------
  393. / Given a namespace path bind to it returning the shell object
  394. /
  395. / In:
  396. / pszPath -> path to bind to
  397. / riid = interface to request
  398. / ppvObject -> receives the object pointer
  399. /
  400. / Out:
  401. / HRESULT
  402. /----------------------------------------------------------------------------*/
  403. HRESULT BindToPath(LPWSTR pszPath, REFIID riid, LPVOID* ppObject)
  404. {
  405. HRESULT hres;
  406. IShellFolder* psfDesktop = NULL;
  407. LPITEMIDLIST pidl = NULL;
  408. //TraceEnter(TRACE_VIEW, "BindToPath");
  409. hres = CoCreateInstance(CLSID_ShellDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&psfDesktop);
  410. if (FAILED(hres))
  411. {
  412. //FailGracefully(hres, "Failed to get IShellFolder for the desktop object");
  413. goto exit_gracefully;
  414. }
  415. hres = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
  416. if (FAILED(hres))
  417. {
  418. //FailGracefully(hres, "Failed when getting root path of DS");
  419. goto exit_gracefully;
  420. }
  421. if ( ILIsEmpty(pidl) )
  422. {
  423. //TraceMsg("PIDL is desktop, therefore just QIing for interface");
  424. hres = psfDesktop->QueryInterface(riid, ppObject);
  425. }
  426. else
  427. {
  428. //TraceMsg("Binding to IDLIST via BindToObject");
  429. hres = psfDesktop->BindToObject(pidl, NULL, riid, ppObject);
  430. }
  431. exit_gracefully:
  432. if ( FAILED(hres) )
  433. *ppObject = NULL;
  434. if (psfDesktop)
  435. psfDesktop->Release();
  436. ILFree(pidl);
  437. pidl = NULL;
  438. return hres;
  439. }
  440. #endif
  441. HRESULT _GetDirectorySF(IShellFolder **ppsf)
  442. {
  443. HRESULT hres;
  444. IShellFolder *psf = NULL;
  445. IDsFolderInternalAPI *pdfi = NULL;
  446. #if DOWNLEVEL_SHELL
  447. IPersistFolder* ppf = NULL;
  448. #endif
  449. DWORD _dwFlags = 0;
  450. //TraceEnter(TRACE_VIEW, "CDsQuery::GetDirectorySF");
  451. #if !DOWNLEVEL_SHELL
  452. // just bind to the path if this is not a downlevel shell.
  453. hres = BindToPath(c_szDsMagicPath, IID_IShellFolder, (void **)&psf);
  454. if (FAILED(hres))
  455. {
  456. //FailGracefully(hres, "Failed to get IShellFolder view of the DS namespace");
  457. goto exit_gracefully;
  458. }
  459. #else
  460. // on the downlevel shell we need to CoCreate the IShellFolder implementation for the
  461. // DS namespace and initialize it manually
  462. hres = CoCreateInstance(CLSID_MicrosoftDS, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (void **)&psf);
  463. if (FAILED(hres))
  464. {
  465. //FailGracefully(hres, "Failed to get the IShellFolder interface we need");
  466. goto exit_gracefully;
  467. }
  468. if ( SUCCEEDED(psf->QueryInterface(IID_IPersistFolder, (void **)&ppf)) )
  469. {
  470. ITEMIDLIST idl = { 0 };
  471. ppf->Initialize(&idl); // it calls ILCLone, so we are safe with stack stuff
  472. ppf->Release();
  473. }
  474. #endif
  475. // using the internal API set all the parameters for dsfolder
  476. hres = psf->QueryInterface(IID_IDsFolderInternalAPI, (void **)&pdfi);
  477. if (FAILED(hres))
  478. {
  479. //FailGracefully(hres, "Failed to get the IDsFolderInternalAPI");
  480. goto exit_gracefully;
  481. }
  482. pdfi->SetAttributePrefix((_dwFlags & DSQPF_ENABLEADMINFEATURES) ? DS_PROP_ADMIN_PREFIX:NULL);
  483. if ( _dwFlags & DSQPF_ENABLEADVANCEDFEATURES )
  484. pdfi->SetProviderFlags(~DSPROVIDER_ADVANCED, DSPROVIDER_ADVANCED);
  485. #if (FALSE)
  486. hres = pdfi->SetComputer(_pServer, _pUserName, _pPassword);
  487. //hres = pdfi->SetComputer(L"marcoc201-50.marcocdev.nttest.microsoft.com", NULL, NULL);
  488. if (FAILED(hres))
  489. {
  490. //FailGracefully(hres, "Failed when setting credential information to be used");
  491. goto exit_gracefully;
  492. }
  493. #endif
  494. exit_gracefully:
  495. if ( SUCCEEDED(hres) )
  496. psf->QueryInterface(IID_IShellFolder, (void **)ppsf);
  497. if (psf)
  498. psf->Release();
  499. if (pdfi)
  500. pdfi->Release();
  501. return hres;
  502. }
  503. /*-----------------------------------------------------------------------------
  504. / CDsQuery::_ADsPathToIdList
  505. / --------------------------
  506. / Convert an ADsPath into an IDLIST that is suitable for the DS ShellFolder
  507. / implementation.
  508. /
  509. / In:
  510. / ppidl -> receives the resulting IDLIST
  511. / pPath = name to be parsed / = NULL then generate only the root of the namespace
  512. / pObjectClass = class to use
  513. /
  514. / Out:
  515. / HRESULT
  516. /----------------------------------------------------------------------------*/
  517. HRESULT _ADsPathToIdList(LPITEMIDLIST* ppidl, LPWSTR pPath, LPWSTR pObjectClass)
  518. {
  519. HRESULT hres;
  520. IBindCtx *pbc = NULL;
  521. IPropertyBag *ppb = NULL;
  522. IShellFolder *psf = NULL;
  523. VARIANT var;
  524. USES_CONVERSION;
  525. *ppidl = NULL; // incase we fail
  526. // if we have an object class then create a bind context containing it
  527. if ( pObjectClass )
  528. {
  529. // create a property bag
  530. hres = ::SHCreatePropertyBag(IID_IPropertyBag, (void **)&ppb);
  531. if (FAILED(hres))
  532. {
  533. //FailGracefully(hres, "Failed to create a property bag");
  534. goto exit_gracefully;
  535. }
  536. // allocate a variant BSTR and set it
  537. V_VT(&var) = VT_BSTR;
  538. V_BSTR(&var) = ::SysAllocString(pObjectClass);
  539. if ( V_BSTR(&var) )
  540. {
  541. ppb->Write(DS_PDN_OBJECTLCASS, &var);
  542. ::VariantClear(&var);
  543. }
  544. hres = CreateBindCtx(0, &pbc);
  545. if (FAILED(hres))
  546. {
  547. //FailGracefully(hres, "Failed to create the BindCtx object");
  548. goto exit_gracefully;
  549. }
  550. hres = pbc->RegisterObjectParam(DS_PDN_PROPERTYBAG, ppb);
  551. if (FAILED(hres))
  552. {
  553. //FailGracefully(hres, "Failed to register the property bag with bindctx");
  554. goto exit_gracefully;
  555. }
  556. }
  557. hres = _GetDirectorySF(&psf);
  558. if (FAILED(hres))
  559. {
  560. //FailGracefully(hres, "Failed to get ShellFolder for DS namespace");
  561. goto exit_gracefully;
  562. }
  563. hres = psf->ParseDisplayName(NULL, pbc, pPath, NULL, ppidl, NULL);
  564. if (FAILED(hres))
  565. {
  566. //FailGracefully(hres, "Failed to parse the name");
  567. goto exit_gracefully;
  568. }
  569. exit_gracefully:
  570. if (pbc)
  571. pbc->Release();
  572. if (ppb)
  573. ppb->Release();
  574. if (psf)
  575. psf->Release();
  576. return hres;
  577. }
  578. #endif // _PROVIDE_CFSTR_SHELLIDLIST_FORMAT
  579. //+----------------------------------------------------------------------------
  580. //
  581. // Class: CDsPropDataObj
  582. //
  583. //-----------------------------------------------------------------------------
  584. CDsPropDataObj::CDsPropDataObj(HWND hwndParentPage,
  585. BOOL fReadOnly) :
  586. m_fReadOnly(fReadOnly),
  587. m_pwszObjName(NULL),
  588. m_pwszObjClass(NULL),
  589. m_hwndParent(hwndParentPage),
  590. m_uRefs(1),
  591. m_pSelectionList(NULL)
  592. {
  593. #ifdef _DEBUG
  594. strcpy(szClass, "CDsPropDataObj");
  595. #endif
  596. dspDebugOut((DEB_USER15, "dsprop's CDsPropDataObj::CDsPropDataObj 0x%x\n", this));
  597. }
  598. CDsPropDataObj::~CDsPropDataObj(void)
  599. {
  600. if (m_pwszObjName)
  601. {
  602. delete m_pwszObjName;
  603. }
  604. if (m_pwszObjClass)
  605. {
  606. delete m_pwszObjClass;
  607. }
  608. if (m_pSelectionList != NULL)
  609. {
  610. for (ULONG idx = 0; idx < m_pSelectionList->cItems; idx++)
  611. {
  612. if (m_pSelectionList->aDsSelection[idx].pwzName != NULL)
  613. {
  614. delete[] m_pSelectionList->aDsSelection[idx].pwzName;
  615. m_pSelectionList->aDsSelection[idx].pwzName = NULL;
  616. }
  617. if (m_pSelectionList->aDsSelection[idx].pwzADsPath != NULL)
  618. {
  619. delete[] m_pSelectionList->aDsSelection[idx].pwzADsPath;
  620. m_pSelectionList->aDsSelection[idx].pwzADsPath = NULL;
  621. }
  622. if (m_pSelectionList->aDsSelection[idx].pwzClass != NULL)
  623. {
  624. delete[] m_pSelectionList->aDsSelection[idx].pwzClass;
  625. m_pSelectionList->aDsSelection[idx].pwzClass = NULL;
  626. }
  627. }
  628. delete m_pSelectionList;
  629. m_pSelectionList = NULL;
  630. }
  631. }
  632. //+----------------------------------------------------------------------------
  633. //
  634. // Method: CDsPropDataObj::Init
  635. //
  636. // Synopsis: Second phase of object creation where failable operations are
  637. // performed.
  638. //
  639. //-----------------------------------------------------------------------------
  640. HRESULT CDsPropDataObj::Init(LPWSTR pwszObjName, LPWSTR pwszClass)
  641. {
  642. if (!pwszObjName || *pwszObjName == L'\0')
  643. {
  644. return E_INVALIDARG;
  645. }
  646. if (!pwszClass || *pwszClass == L'\0')
  647. {
  648. return E_INVALIDARG;
  649. }
  650. m_pwszObjName = new WCHAR[wcslen(pwszObjName) + 1];
  651. CHECK_NULL(m_pwszObjName, return E_OUTOFMEMORY);
  652. wcscpy(m_pwszObjName, pwszObjName);
  653. m_pwszObjClass = new WCHAR[wcslen(pwszClass) + 1];
  654. CHECK_NULL(m_pwszObjClass, return E_OUTOFMEMORY);
  655. wcscpy(m_pwszObjClass, pwszClass);
  656. return S_OK;
  657. }
  658. //+----------------------------------------------------------------------------
  659. //
  660. // Method: CDsPropDataObj::Init
  661. //
  662. // Synopsis: Second phase of object creation where failable operations are
  663. // performed.
  664. //
  665. //-----------------------------------------------------------------------------
  666. HRESULT CDsPropDataObj::Init(PDS_SELECTION_LIST pSelectionList)
  667. {
  668. HRESULT hr = S_OK;
  669. if (m_pSelectionList != NULL)
  670. {
  671. for (ULONG idx = 0; idx < m_pSelectionList->cItems; idx++)
  672. {
  673. if (m_pSelectionList->aDsSelection[idx].pwzName != NULL)
  674. {
  675. delete[] m_pSelectionList->aDsSelection[idx].pwzName;
  676. m_pSelectionList->aDsSelection[idx].pwzName = NULL;
  677. }
  678. if (m_pSelectionList->aDsSelection[idx].pwzADsPath != NULL)
  679. {
  680. delete[] m_pSelectionList->aDsSelection[idx].pwzADsPath;
  681. m_pSelectionList->aDsSelection[idx].pwzADsPath = NULL;
  682. }
  683. if (m_pSelectionList->aDsSelection[idx].pwzClass != NULL)
  684. {
  685. delete[] m_pSelectionList->aDsSelection[idx].pwzClass;
  686. m_pSelectionList->aDsSelection[idx].pwzClass = NULL;
  687. }
  688. }
  689. delete m_pSelectionList;
  690. m_pSelectionList = NULL;
  691. }
  692. m_pSelectionList = pSelectionList;
  693. return hr;
  694. }
  695. //+----------------------------------------------------------------------------
  696. //
  697. // Method: CDsPropDataObj::IDataObject::GetData
  698. //
  699. // Synopsis: Returns data, in this case the Prop Page format data.
  700. //
  701. //-----------------------------------------------------------------------------
  702. STDMETHODIMP
  703. CDsPropDataObj::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
  704. {
  705. TRACE(CDsPropDataObj,GetData);
  706. if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
  707. {
  708. return E_INVALIDARG;
  709. }
  710. if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
  711. {
  712. return DV_E_TYMED;
  713. }
  714. if (pFormatEtc->cfFormat == g_cfDsObjectNames)
  715. {
  716. // Return the object name and class.
  717. //
  718. size_t cbPath = sizeof(WCHAR) * (wcslen(m_pwszObjName) + 1);
  719. size_t cbClass = sizeof(WCHAR) * (wcslen(m_pwszObjClass) + 1);
  720. size_t cbStruct = sizeof(DSOBJECTNAMES);
  721. LPDSOBJECTNAMES pDSObj;
  722. pDSObj = (LPDSOBJECTNAMES)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  723. cbStruct + cbPath + cbClass);
  724. if (pDSObj == NULL)
  725. {
  726. return STG_E_MEDIUMFULL;
  727. }
  728. pDSObj->clsidNamespace = CLSID_MicrosoftDS;
  729. pDSObj->cItems = 1;
  730. pDSObj->aObjects[0].offsetName = static_cast<DWORD>(cbStruct);
  731. pDSObj->aObjects[0].offsetClass = static_cast<DWORD>(cbStruct + cbPath);
  732. if (m_fReadOnly)
  733. {
  734. pDSObj->aObjects[0].dwFlags = DSOBJECT_READONLYPAGES;
  735. }
  736. pDSObj->aObjects[0].dwProviderFlags = 0;
  737. dspDebugOut((DEB_ITRACE, "returning %ws and %ws\n", m_pwszObjName, m_pwszObjClass));
  738. wcscpy((PWSTR)((BYTE *)pDSObj + cbStruct), m_pwszObjName);
  739. wcscpy((PWSTR)((BYTE *)pDSObj + cbStruct + cbPath), m_pwszObjClass);
  740. pMedium->hGlobal = (HGLOBAL)pDSObj;
  741. }
  742. else if (pFormatEtc->cfFormat == g_cfDsPropCfg)
  743. {
  744. // Return the property sheet notification info. In this case, it is
  745. // the invokding sheet's hwnd.
  746. //
  747. PPROPSHEETCFG pSheetCfg;
  748. pSheetCfg = (PPROPSHEETCFG)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  749. sizeof(PROPSHEETCFG));
  750. if (pSheetCfg == NULL)
  751. {
  752. return STG_E_MEDIUMFULL;
  753. }
  754. ZeroMemory(pSheetCfg, sizeof(PROPSHEETCFG));
  755. pSheetCfg->hwndParentSheet = m_hwndParent;
  756. pMedium->hGlobal = (HGLOBAL)pSheetCfg;
  757. }
  758. #ifdef _PROVIDE_CFSTR_SHELLIDLIST_FORMAT
  759. else if (pFormatEtc->cfFormat == g_cfShellIDListArray)
  760. {
  761. // Return the CFSTR_SHELLIDLIST clipboard format
  762. LPITEMIDLIST pidl = NULL;
  763. HRESULT hr = _ADsPathToIdList(&pidl, m_pwszObjName, m_pwszObjClass);
  764. if (FAILED(hr))
  765. {
  766. return hr;
  767. }
  768. if (pidl == NULL)
  769. {
  770. return E_UNEXPECTED;
  771. }
  772. // we have a valid pidl, need to allocate global memory
  773. // get the count of bytes
  774. UINT cBytes = sizeof(CIDA) + ILGetSize(pidl) + sizeof(DWORD);
  775. LPIDA pIDArray = (LPIDA)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  776. cBytes);
  777. if (pIDArray == NULL)
  778. {
  779. hr = STG_E_MEDIUMFULL;
  780. }
  781. else
  782. {
  783. hr = S_OK;
  784. pIDArray->cidl = 1;
  785. pIDArray->aoffset[0] = sizeof(CIDA);
  786. LPBYTE pMem = ((LPBYTE)pIDArray) + sizeof(CIDA);
  787. // copy the pidl past the CIDA
  788. ::memcpy(pMem, pidl, ILGetSize(pidl));
  789. pMem += ILGetSize(pidl);
  790. // make sure we have a NULL dword past the list
  791. ::ZeroMemory(pMem, sizeof(DWORD));
  792. pMedium->hGlobal = (HGLOBAL)pIDArray;
  793. }
  794. SHILFree(pidl);
  795. if (FAILED(hr))
  796. {
  797. return hr;
  798. }
  799. }
  800. #endif // _PROVIDE_CFSTR_SHELLIDLIST_FORMAT
  801. else if (pFormatEtc->cfFormat == g_cfDsSelList)
  802. {
  803. //
  804. // Return the selection list for ObjectPicker
  805. //
  806. PDS_SELECTION_LIST pSelectionList = NULL;
  807. if (m_pSelectionList != NULL)
  808. {
  809. size_t cbRequired = sizeof(DS_SELECTION_LIST) +
  810. (sizeof(DS_SELECTION) * (m_pSelectionList->cItems - 1));
  811. pSelectionList = (PDS_SELECTION_LIST)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  812. cbRequired);
  813. if (pSelectionList == NULL)
  814. {
  815. return STG_E_MEDIUMFULL;
  816. }
  817. ZeroMemory(pSelectionList, cbRequired);
  818. memcpy(pSelectionList, m_pSelectionList, cbRequired);
  819. pMedium->hGlobal = (HGLOBAL)pSelectionList;
  820. }
  821. else
  822. {
  823. size_t cbRequired = sizeof(DS_SELECTION_LIST);
  824. pSelectionList = (PDS_SELECTION_LIST)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  825. cbRequired);
  826. if (pSelectionList == NULL)
  827. {
  828. return STG_E_MEDIUMFULL;
  829. }
  830. ZeroMemory(pSelectionList, cbRequired);
  831. pMedium->hGlobal = (HGLOBAL)pSelectionList;
  832. }
  833. }
  834. else
  835. {
  836. return DV_E_FORMATETC;
  837. }
  838. pMedium->tymed = TYMED_HGLOBAL;
  839. pMedium->pUnkForRelease = NULL;
  840. return S_OK;
  841. }
  842. //+----------------------------------------------------------------------------
  843. //
  844. // Member: CDsPropDataObj::IUnknown::QueryInterface
  845. //
  846. // Synopsis: Returns requested interface pointer
  847. //
  848. //-----------------------------------------------------------------------------
  849. STDMETHODIMP
  850. CDsPropDataObj::QueryInterface(REFIID riid, void ** ppvObject)
  851. {
  852. TRACE2(CDsPropDataObj,QueryInterface);
  853. if (IID_IUnknown == riid)
  854. {
  855. *ppvObject = (IUnknown *)this;
  856. }
  857. else if (IID_IDataObject == riid)
  858. {
  859. *ppvObject = this;
  860. }
  861. else
  862. {
  863. *ppvObject = NULL;
  864. return E_NOINTERFACE;
  865. }
  866. AddRef();
  867. return S_OK;
  868. }
  869. //+----------------------------------------------------------------------------
  870. //
  871. // Member: CDsPropDataObj::IUnknown::AddRef
  872. //
  873. // Synopsis: increments reference count
  874. //
  875. // Returns: the reference count
  876. //
  877. //-----------------------------------------------------------------------------
  878. STDMETHODIMP_(ULONG)
  879. CDsPropDataObj::AddRef(void)
  880. {
  881. dspDebugOut((DEB_USER15, "CDsPropDataObj::AddRef refcount going in %d\n", m_uRefs));
  882. return InterlockedIncrement((long *)&m_uRefs);
  883. }
  884. //+----------------------------------------------------------------------------
  885. //
  886. // Member: CDsPropDataObj::IUnknown::Release
  887. //
  888. // Synopsis: Decrements the object's reference count and frees it when
  889. // no longer referenced.
  890. //
  891. // Returns: zero if the reference count is zero or non-zero otherwise
  892. //
  893. //-----------------------------------------------------------------------------
  894. STDMETHODIMP_(ULONG)
  895. CDsPropDataObj::Release(void)
  896. {
  897. dspDebugOut((DEB_USER15, "CDsPropDataObj::Release ref count going in %d\n", m_uRefs));
  898. unsigned long uTmp;
  899. if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
  900. {
  901. delete this;
  902. }
  903. return uTmp;
  904. }
  905. //+----------------------------------------------------------------------------
  906. //
  907. // Function: AddPageProc
  908. //
  909. // Synopsis: The IShellPropSheetExt->AddPages callback.
  910. //
  911. //-----------------------------------------------------------------------------
  912. //BOOL CALLBACK
  913. //AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall)
  914. //{
  915. // HRESULT hr;
  916. // hr = ((LPPROPERTYSHEETCALLBACK)pCall)->AddPage(hPage);
  917. // return hr == S_OK;
  918. //}
  919. //+----------------------------------------------------------------------------
  920. //
  921. // Function: PostPropSheet, internal; used by CDsPropPageBase members.
  922. //
  923. // Synopsis: Creates a property sheet for the named object using MMC's
  924. // IPropertySheetProvider so that extension snapins can add pages.
  925. //
  926. // Arguments: [pwzObjDN] - the DN path of the DS object.
  927. // [pParentPage] - the invoking page's this pointer.
  928. // [fReadOnly] - defaults to FALSE.
  929. //
  930. //-----------------------------------------------------------------------------
  931. HRESULT
  932. PostPropSheet(PWSTR pwzObjDN, CDsPropPageBase * pParentPage, BOOL fReadOnly)
  933. {
  934. HRESULT hr;
  935. CComPtr<IADsPathname> spADsPath;
  936. dspDebugOut((DEB_ITRACE, "PostPropSheet passed path %ws\n", pwzObjDN));
  937. hr = pParentPage->GetADsPathname(spADsPath);
  938. CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr);
  939. hr = spADsPath->Set(pwzObjDN, ADS_SETTYPE_DN);
  940. CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr);
  941. hr = spADsPath->put_EscapedMode(ADS_ESCAPEDMODE_ON);
  942. CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr);
  943. BSTR bstrEscapedPath;
  944. hr = spADsPath->Retrieve(ADS_FORMAT_X500_DN, &bstrEscapedPath);
  945. CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr);
  946. dspDebugOut((DEB_ITRACE, "PostPropSheet escaped path %ws\n", bstrEscapedPath));
  947. hr = PostPropSheetWorker(pParentPage, bstrEscapedPath,
  948. pParentPage->m_pWPTDataObj,
  949. pParentPage->GetHWnd(), fReadOnly);
  950. SysFreeString(bstrEscapedPath);
  951. CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr);
  952. return S_OK;
  953. }
  954. //+----------------------------------------------------------------------------
  955. //
  956. // Function: PostADsPropSheet, exported from DLL.
  957. //
  958. // Synopsis: Creates a property sheet for the named object using MMC's
  959. // IPropertySheetProvider so that extension snapins can add pages.
  960. //
  961. // Arguments: [pwzObjDN] - the full LDAP DN of the DS object.
  962. // [pParentObj] - the invoking page's MMC data object pointer.
  963. // [hwndParent] - the invoking page's window handle.
  964. // [fReadOnly] - defaults to FALSE.
  965. //
  966. //-----------------------------------------------------------------------------
  967. HRESULT
  968. PostADsPropSheet(PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent,
  969. BOOL fReadOnly)
  970. {
  971. return PostPropSheetWorker(NULL, pwzObjDN, pParentObj, hwndParent, fReadOnly);
  972. }
  973. //+----------------------------------------------------------------------------
  974. //
  975. // Function: PostADsPropSheet, exported from DLL.
  976. //
  977. // Synopsis: Creates a property sheet for the named object using MMC's
  978. // IPropertySheetProvider so that extension snapins can add pages.
  979. //
  980. // Arguments: [pwzObjDN] - the full LDAP DN of the DS object.
  981. // [pParentObj] - the invoking page's MMC data object pointer.
  982. // [hwndParent] - the invoking page's window handle.
  983. // [hNotifyObj] - the handle to the notify object window
  984. // [fReadOnly] - defaults to FALSE.
  985. //
  986. //-----------------------------------------------------------------------------
  987. HRESULT
  988. PostADsPropSheet(PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent,
  989. HWND hNotifyObj, BOOL fReadOnly)
  990. {
  991. return PostPropSheetWorker(NULL, pwzObjDN, pParentObj, hwndParent, hNotifyObj, fReadOnly);
  992. }
  993. //+----------------------------------------------------------------------------
  994. //
  995. // Function: CreateSecondaryPageInfo
  996. //
  997. // Synopsis: Helper for posting a second sheet.
  998. //
  999. //-----------------------------------------------------------------------------
  1000. PDSA_SEC_PAGE_INFO CreateSecondaryPageInfo(HWND hwndParent,
  1001. LPCWSTR lpszTitle,
  1002. LPCWSTR lpszName,
  1003. LPCWSTR lpszClass,
  1004. BOOL fReadOnly)
  1005. {
  1006. // determine memory allocation needs
  1007. size_t nTitleLen = wcslen(lpszTitle)+1;
  1008. size_t nNameLen = wcslen(lpszName)+1;
  1009. size_t nClassLen = wcslen(lpszClass)+1;
  1010. size_t cbStruct = sizeof(DSA_SEC_PAGE_INFO);
  1011. size_t cbStrings = (nTitleLen+nNameLen+nClassLen)*sizeof(WCHAR);
  1012. // allocate memory
  1013. PDSA_SEC_PAGE_INFO pDsaSecondaryPageInfo =
  1014. (PDSA_SEC_PAGE_INFO) ::LocalAlloc(LPTR, (cbStruct + cbStrings));
  1015. if (pDsaSecondaryPageInfo == NULL)
  1016. return NULL;
  1017. // set members
  1018. pDsaSecondaryPageInfo->hwndParentSheet = hwndParent;
  1019. // title string just at the end of the DSA_SEC_PAGE_INFO struct
  1020. pDsaSecondaryPageInfo->offsetTitle = static_cast<DWORD>(cbStruct);
  1021. wcscpy((LPWSTR)((BYTE *)pDsaSecondaryPageInfo + pDsaSecondaryPageInfo->offsetTitle), lpszTitle);
  1022. DSOBJECTNAMES* pDsObjectNames = &(pDsaSecondaryPageInfo->dsObjectNames);
  1023. pDsObjectNames->cItems = 1;
  1024. DSOBJECT* pDsObject = &(pDsObjectNames->aObjects[0]);
  1025. pDsObject->dwFlags = fReadOnly ? DSOBJECT_READONLYPAGES : 0;
  1026. pDsObject->dwProviderFlags = 0; // not set
  1027. // DSOBJECT strings past the DSA_SEC_PAGE_INFO strings
  1028. // offsets are relative to the beginning of DSOBJECT struct
  1029. pDsObject->offsetName = static_cast<DWORD>(sizeof(DSOBJECT) + nTitleLen*sizeof(WCHAR));
  1030. wcscpy((LPWSTR)((BYTE *)pDsObject + pDsObject->offsetName), lpszName);
  1031. pDsObject->offsetClass = static_cast<DWORD>(pDsObject->offsetName + nNameLen*sizeof(WCHAR));
  1032. wcscpy((LPWSTR)((BYTE *)pDsObject + pDsObject->offsetClass), lpszClass);
  1033. return pDsaSecondaryPageInfo;
  1034. }
  1035. //+----------------------------------------------------------------------------
  1036. //
  1037. // Function: _WindowsPathFromDN
  1038. //
  1039. // Synopsis: Helper to translate a DN into a windows path
  1040. // for example, from "CN=Joe,OU=test,DN=acme,DN=com"
  1041. // to "LDAP://DN=com/DN=acme/OU=test/CN=Joe
  1042. //
  1043. //-----------------------------------------------------------------------------
  1044. HRESULT
  1045. _WindowsPathFromDN(IN PWSTR pwzObjDN, OUT BSTR* pbstrWindowsPath)
  1046. {
  1047. // create a path cracker object
  1048. CComPtr<IADsPathname> spIADsPathname;
  1049. HRESULT hr = ::CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER,
  1050. IID_IADsPathname, (void**)&spIADsPathname);
  1051. if (FAILED(hr))
  1052. {
  1053. return hr;
  1054. }
  1055. spIADsPathname->Set(pwzObjDN,ADS_SETTYPE_DN);
  1056. hr = spIADsPathname->Retrieve(ADS_FORMAT_WINDOWS, pbstrWindowsPath);
  1057. return hr;
  1058. }
  1059. //+----------------------------------------------------------------------------
  1060. //
  1061. // Function: PostPropSheetWorker
  1062. //
  1063. // Synopsis: Helper for posting a second sheet.
  1064. //
  1065. //-----------------------------------------------------------------------------
  1066. HRESULT
  1067. PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN,
  1068. IDataObject * pParentObj, HWND hwndParent, BOOL fReadOnly)
  1069. {
  1070. HWND hNotifyObj = NULL;
  1071. ::SendMessage(hwndParent, WM_ADSPROP_PAGE_GET_NOTIFY, (WPARAM)&hNotifyObj, 0);
  1072. if (hNotifyObj == NULL)
  1073. return E_UNEXPECTED;
  1074. return PostPropSheetWorker(pParentPage, pwzObjDN, pParentObj, hwndParent, hNotifyObj, fReadOnly);
  1075. }
  1076. HRESULT
  1077. PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN,
  1078. IDataObject * pParentObj, HWND hwndParent, HWND hNotifyObj, BOOL fReadOnly)
  1079. {
  1080. HRESULT hr = S_OK;
  1081. CDsPropDataObj * pDataObj = NULL;
  1082. BSTR bstrName = NULL, bstrClass = NULL;
  1083. BSTR bstrPath = NULL;
  1084. CStrW strADsPath;
  1085. CWaitCursor cWait;
  1086. BOOL fShell = FALSE;
  1087. PWSTR pwszName = NULL;
  1088. CComPtr<IADsPathname> spPathCracker;
  1089. hr = AddLDAPPrefix(pParentPage, pwzObjDN, strADsPath);
  1090. CHECK_HRESULT(hr, return hr);
  1091. #ifdef DSADMIN
  1092. // Check to see if a sheet is already posted for this object.
  1093. //
  1094. if (FindSheet(const_cast<PWSTR>((LPCWSTR)strADsPath)))
  1095. {
  1096. return S_OK;
  1097. }
  1098. #endif // DSADMIN
  1099. //
  1100. // Activate the object as a test of the path's correctness. Get the
  1101. // naming attribute, class, and schema class GUID while we are at it.
  1102. //
  1103. IADs * pObj = NULL;
  1104. dspDebugOut((DEB_USER1, "Opening prop sheet on '%S'\n", strADsPath));
  1105. hr = ADsOpenObject(const_cast<PWSTR>((LPCWSTR)strADsPath), NULL, NULL,
  1106. ADS_SECURE_AUTHENTICATION, IID_IADs, (PVOID *)&pObj);
  1107. if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
  1108. {
  1109. // Display the object-not-found error message.
  1110. //
  1111. TCHAR tzFormat[MAX_ERRORMSG+1], tzTitle[MAX_TITLE+1];
  1112. PTSTR ptzObjDN, ptzMsg;
  1113. LOAD_STRING(IDS_INVALID_NAME_FORMAT, tzFormat, MAX_ERRORMSG, return E_OUTOFMEMORY);
  1114. LOAD_STRING(IDS_MSG_TITLE, tzTitle, MAX_TITLE, return E_OUTOFMEMORY);
  1115. if (!UnicodeToTchar(pwzObjDN, &ptzObjDN))
  1116. {
  1117. return E_OUTOFMEMORY;
  1118. }
  1119. ptzMsg = new TCHAR[_tcslen(ptzObjDN) + _tcslen(tzFormat) + 1];
  1120. CHECK_NULL(ptzMsg, delete ptzObjDN; return E_OUTOFMEMORY);
  1121. wsprintf(ptzMsg, tzFormat, ptzObjDN);
  1122. delete ptzObjDN;
  1123. MessageBox(hwndParent, ptzMsg, tzTitle,
  1124. MB_ICONEXCLAMATION | MB_OK);
  1125. goto Cleanup;
  1126. }
  1127. if (!CHECK_ADS_HR(&hr, hwndParent))
  1128. {
  1129. goto Cleanup;
  1130. }
  1131. hr = pObj->get_ADsPath(&bstrPath);
  1132. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1133. hr = pObj->get_Class(&bstrClass);
  1134. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1135. pObj->Release();
  1136. pObj = NULL;
  1137. hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER,
  1138. IID_IADsPathname, (PVOID *)&spPathCracker);
  1139. if (SUCCEEDED(hr))
  1140. {
  1141. hr = spPathCracker->Set(bstrPath, ADS_SETTYPE_FULL);
  1142. if (SUCCEEDED(hr))
  1143. {
  1144. LONG lEscapeMode = 0;
  1145. hr = spPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1146. if (SUCCEEDED(hr))
  1147. {
  1148. hr = spPathCracker->get_EscapedMode(&lEscapeMode);
  1149. if (SUCCEEDED(hr))
  1150. {
  1151. hr = spPathCracker->put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
  1152. if (SUCCEEDED(hr))
  1153. {
  1154. hr = spPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName);
  1155. dspAssert(bstrName != NULL);
  1156. }
  1157. }
  1158. }
  1159. //
  1160. // Set the display back to full so we don't mess up other instances
  1161. //
  1162. hr = spPathCracker->SetDisplayType(ADS_DISPLAY_FULL);
  1163. hr = spPathCracker->put_EscapedMode(lEscapeMode);
  1164. }
  1165. }
  1166. //
  1167. // If something went wrong with the path cracker just hack the name
  1168. // ourselves.
  1169. //
  1170. if (bstrName == NULL)
  1171. {
  1172. hr = pObj->get_Name(&bstrName);
  1173. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1174. pwszName = wcschr(bstrName, L'=');
  1175. if (pwszName && (*pwszName != L'\0'))
  1176. {
  1177. pwszName++;
  1178. }
  1179. else
  1180. {
  1181. pwszName = bstrName;
  1182. }
  1183. }
  1184. else
  1185. {
  1186. pwszName = bstrName;
  1187. }
  1188. bool fDomAdmin = false;
  1189. //
  1190. // Are we being called from the shell or from the admin snapin?
  1191. //
  1192. if (!pParentObj)
  1193. {
  1194. fShell = TRUE;
  1195. }
  1196. else
  1197. {
  1198. FORMATETC fmte = {g_cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1199. STGMEDIUM ObjMedium;
  1200. PDSDISPLAYSPECOPTIONS pDsDispSpecOptions;
  1201. PWSTR pwzAttribPrefix;
  1202. hr = pParentObj->GetData(&fmte, &ObjMedium);
  1203. if (DV_E_FORMATETC == hr)
  1204. {
  1205. fShell = TRUE;
  1206. }
  1207. else
  1208. {
  1209. CHECK_HRESULT(hr, return hr);
  1210. pDsDispSpecOptions = (PDSDISPLAYSPECOPTIONS)ObjMedium.hGlobal;
  1211. pwzAttribPrefix = (PWSTR)ByteOffset(pDsDispSpecOptions,
  1212. pDsDispSpecOptions->offsetAttribPrefix);
  1213. if (_wcsicmp(pwzAttribPrefix, DS_PROP_ADMIN_PREFIX) != 0)
  1214. {
  1215. fShell = TRUE;
  1216. }
  1217. ReleaseStgMedium(&ObjMedium);
  1218. }
  1219. fmte.cfFormat = g_cfDsObjectNames;
  1220. fmte.ptd = NULL;
  1221. fmte.dwAspect = DVASPECT_CONTENT;
  1222. fmte.lindex = -1;
  1223. fmte.tymed = TYMED_HGLOBAL;
  1224. hr = pParentObj->GetData(&fmte, &ObjMedium);
  1225. CHECK_HRESULT(hr, return hr);
  1226. LPDSOBJECTNAMES pDsObjectNames = (LPDSOBJECTNAMES)ObjMedium.hGlobal;
  1227. if (IsEqualCLSID(pDsObjectNames->clsidNamespace, CLSID_DomainAdmin))
  1228. {
  1229. fDomAdmin = true;
  1230. }
  1231. ReleaseStgMedium(&ObjMedium);
  1232. }
  1233. if (fShell)
  1234. {
  1235. // the shell uses windows paths, so we have to convert the DN
  1236. //
  1237. BSTR bstrWindowsPath = NULL;
  1238. CStrW strADsWindowsPath;
  1239. hr = _WindowsPathFromDN(pwzObjDN, &bstrWindowsPath);
  1240. if (SUCCEEDED(hr))
  1241. {
  1242. strADsWindowsPath = bstrWindowsPath;
  1243. ::SysFreeString(bstrWindowsPath);
  1244. }
  1245. else
  1246. {
  1247. // if we fail, try to survive with the regular ADSI path
  1248. strADsWindowsPath = strADsPath;
  1249. }
  1250. // try to see it a sheet with this title is already up
  1251. if (FindSheet(const_cast<PWSTR>((LPCWSTR)strADsWindowsPath)))
  1252. {
  1253. goto Cleanup;
  1254. }
  1255. // Create the intermediary data object.
  1256. //
  1257. pDataObj = new CDsPropDataObj(hwndParent, fReadOnly);
  1258. CHECK_NULL_REPORT(pDataObj, hwndParent, goto Cleanup;);
  1259. hr = pDataObj->Init(const_cast<PWSTR>((LPCWSTR)strADsWindowsPath), bstrClass);
  1260. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1261. //
  1262. // Have the DS shell folder extension post the sheet.
  1263. //
  1264. IDsFolderProperties * pdfp = NULL;
  1265. hr = CoCreateInstance(CLSID_DsFolderProperties, NULL, CLSCTX_INPROC_SERVER,
  1266. IID_IDsFolderProperties, (void **)&pdfp);
  1267. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1268. hr = pdfp->ShowProperties(hwndParent, pDataObj);
  1269. CHECK_HRESULT_REPORT(hr, hwndParent, ;);
  1270. pdfp->Release();
  1271. goto Cleanup;
  1272. }
  1273. // called from DS Admin, delegate to it
  1274. {
  1275. //
  1276. // First crack the path to something readable
  1277. //
  1278. PWSTR pwzCanEx = NULL;
  1279. hr = CrackName(pwzObjDN, &pwzCanEx, GET_OBJ_CAN_NAME_EX, hwndParent);
  1280. if (SUCCEEDED(hr) &&
  1281. pwzCanEx)
  1282. {
  1283. PWSTR pwzSlash = wcsrchr(pwzCanEx, L'\n');
  1284. if (pwzSlash)
  1285. {
  1286. pwszName = pwzSlash + 1;
  1287. }
  1288. else
  1289. {
  1290. pwszName = pwzCanEx;
  1291. }
  1292. }
  1293. hr = S_OK;
  1294. PDSA_SEC_PAGE_INFO pDsaSecondaryPageInfo =
  1295. CreateSecondaryPageInfo(hwndParent, pwszName, pwzObjDN, bstrClass, fReadOnly);
  1296. if (pDsaSecondaryPageInfo == NULL)
  1297. {
  1298. hr = E_OUTOFMEMORY;
  1299. CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;);
  1300. }
  1301. PWSTR pwzDC = NULL;
  1302. if (fDomAdmin)
  1303. {
  1304. //
  1305. // Get the DC name to send to the snap in.
  1306. //
  1307. CStrW strDC;
  1308. hr = GetLdapServerName(pParentPage->m_pDsObj, strDC);
  1309. if (SUCCEEDED(hr))
  1310. {
  1311. pwzDC = (PWSTR) LocalAlloc(LPTR, (strDC.GetLength() + 1) * sizeof(WCHAR));
  1312. CHECK_NULL_REPORT(pwzDC, hwndParent, goto Cleanup);
  1313. wcscpy(pwzDC, strDC);
  1314. }
  1315. }
  1316. ::SendMessage(hNotifyObj, WM_ADSPROP_SHEET_CREATE, (WPARAM)pDsaSecondaryPageInfo, (LPARAM)pwzDC);
  1317. }
  1318. cWait.SetOld();
  1319. Cleanup:
  1320. if (pObj)
  1321. {
  1322. pObj->Release();
  1323. }
  1324. if (pDataObj)
  1325. {
  1326. // The prop sheet holds on to the dataobj, so don't delete it here.
  1327. //
  1328. dspDebugOut((DEB_USER15, "PostPropSheet releasing data obj 0x%x\n", pDataObj));
  1329. pDataObj->Release();
  1330. }
  1331. if (bstrName)
  1332. {
  1333. SysFreeString(bstrName);
  1334. }
  1335. if (bstrClass)
  1336. {
  1337. SysFreeString(bstrClass);
  1338. }
  1339. return hr;
  1340. }
  1341. //+----------------------------------------------------------------------------
  1342. //
  1343. // Function: GetDomainScope
  1344. //
  1345. // Synopsis: Returns the full LDAP DN of the domain of the current object.
  1346. //
  1347. //-----------------------------------------------------------------------------
  1348. HRESULT GetDomainScope(CDsPropPageBase * pPage, BSTR * pBstrOut)
  1349. {
  1350. PWSTR pwzObjDN;
  1351. HRESULT hr = pPage->SkipPrefix(pPage->GetObjPathName(), &pwzObjDN);
  1352. CHECK_HRESULT(hr, return hr);
  1353. hr = GetObjectsDomain(pPage, pwzObjDN, pBstrOut);
  1354. DO_DEL(pwzObjDN);
  1355. CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr);
  1356. return S_OK;
  1357. }
  1358. //+----------------------------------------------------------------------------
  1359. //
  1360. // Function: GetObjectsDomain
  1361. //
  1362. // Synopsis: Returns the full LDAP DN of the domain of the passed in object.
  1363. //
  1364. //-----------------------------------------------------------------------------
  1365. HRESULT GetObjectsDomain(CDsPropPageBase * pPage, PWSTR pwzObjDN, BSTR * pBstrOut)
  1366. {
  1367. PWSTR pwzDomainDN;
  1368. HRESULT hr;
  1369. hr = CrackName(pwzObjDN, &pwzDomainDN, GET_FQDN_DOMAIN_NAME, pPage->GetHWnd());
  1370. CHECK_HRESULT(hr, return hr);
  1371. CComPtr<IADsPathname> spPathCracker;
  1372. hr = pPage->GetADsPathname(spPathCracker);
  1373. CHECK_HRESULT(hr, return hr);
  1374. hr = spPathCracker->Set(g_wzLDAPProvider, ADS_SETTYPE_PROVIDER);
  1375. CHECK_HRESULT(hr, return hr);
  1376. CStrW strDC;
  1377. hr = GetLdapServerName(pPage->m_pDsObj, strDC);
  1378. CHECK_HRESULT(hr, return hr);
  1379. hr = spPathCracker->Set(const_cast<PWSTR>((LPCWSTR)strDC), ADS_SETTYPE_SERVER);
  1380. CHECK_HRESULT(hr, return hr);
  1381. hr = spPathCracker->Set(pwzDomainDN, ADS_SETTYPE_DN);
  1382. LocalFreeStringW(&pwzDomainDN);
  1383. CHECK_HRESULT(hr, return hr);
  1384. hr = spPathCracker->Retrieve(ADS_FORMAT_X500, pBstrOut);
  1385. CHECK_HRESULT(hr, return hr);
  1386. dspDebugOut((DEB_USER1, "Object's domain is: '%S'\n", *pBstrOut));
  1387. return S_OK;
  1388. }
  1389. //+----------------------------------------------------------------------------
  1390. //
  1391. // Function: CheckRegisterClipFormats
  1392. //
  1393. // Synopsis: If the clipboard formats haven't been registered yet, register
  1394. // them.
  1395. //
  1396. //-----------------------------------------------------------------------------
  1397. HRESULT
  1398. CheckRegisterClipFormats(void)
  1399. {
  1400. // Check to see if we have our clipboard formats registered, if not then
  1401. // lets do it.
  1402. if (!g_cfDsObjectNames)
  1403. {
  1404. g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  1405. }
  1406. if (!g_cfDsObjectNames)
  1407. {
  1408. ERR_OUT("No clipboard format registered", 0);
  1409. return HRESULT_FROM_WIN32(GetLastError());
  1410. }
  1411. if (!g_cfDsDispSpecOptions)
  1412. {
  1413. g_cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_DISPLAY_SPEC_OPTIONS);
  1414. }
  1415. if (!g_cfDsDispSpecOptions)
  1416. {
  1417. ERR_OUT("No DS Display Spec Options clipboard format registered", 0);
  1418. return HRESULT_FROM_WIN32(GetLastError());
  1419. }
  1420. if (!g_cfShellIDListArray)
  1421. {
  1422. g_cfShellIDListArray = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST);
  1423. }
  1424. if (!g_cfShellIDListArray)
  1425. {
  1426. ERR_OUT("No Shell IDList Array clipboard format registered", 0);
  1427. return HRESULT_FROM_WIN32(GetLastError());
  1428. }
  1429. #ifdef DSADMIN
  1430. if (!g_cfMMCGetNodeType)
  1431. {
  1432. g_cfMMCGetNodeType = (CLIPFORMAT)RegisterClipboardFormat(CCF_NODETYPE);
  1433. }
  1434. if (!g_cfMMCGetNodeType)
  1435. {
  1436. ERR_OUT("No node-type clipboard format registered", 0);
  1437. return HRESULT_FROM_WIN32(GetLastError());
  1438. }
  1439. if (!g_cfDsPropCfg)
  1440. {
  1441. g_cfDsPropCfg = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_PROPSHEETCONFIG);
  1442. }
  1443. if (!g_cfDsPropCfg)
  1444. {
  1445. ERR_OUT("No propsheet cfg clipboard format registered", 0);
  1446. return HRESULT_FROM_WIN32(GetLastError());
  1447. }
  1448. if (!g_cfDsSelList)
  1449. {
  1450. g_cfDsSelList = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
  1451. }
  1452. if (!g_cfDsSelList)
  1453. {
  1454. ERR_OUT("No object picker clipboard format registered", 0);
  1455. return HRESULT_FROM_WIN32(GetLastError());
  1456. }
  1457. if (!g_cfDsMultiSelectProppages)
  1458. {
  1459. g_cfDsMultiSelectProppages = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_MULTISELECTPROPPAGE);
  1460. }
  1461. if (!g_cfDsMultiSelectProppages)
  1462. {
  1463. ERR_OUT("No multi-select proppages clipboard format registered", 0);
  1464. return HRESULT_FROM_WIN32(GetLastError());
  1465. }
  1466. //if (!g_cfMMCGetCoClass)
  1467. //{
  1468. // g_cfMMCGetCoClass = (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
  1469. //}
  1470. //if (!g_cfMMCGetCoClass)
  1471. //{
  1472. // ERR_OUT("No clipboard format registered", 0);
  1473. // return HRESULT_FROM_WIN32(GetLastError());
  1474. //}
  1475. #endif
  1476. return S_OK;
  1477. }
  1478. //+----------------------------------------------------------------------------
  1479. //
  1480. // Function: CrackName
  1481. //
  1482. // Synopsis: Given an object name, returns the requested name. The string
  1483. // allocated to hold this name must be freed using
  1484. // LocalFreeStringW (it and LocalAllocStringW are imported from
  1485. // dsuiext.dll).
  1486. //
  1487. // Operations:
  1488. // GET_OBJ_CAN_NAME - return the object's canonical name.
  1489. // GET_OBJ_CAN_NAME_EX - leaf element delimited by CR character.
  1490. // GET_OBJ_1779_DN - return object's FQDN.
  1491. // GET_OBJ_NT4_NAME - return object's downlevel name (domain\name).
  1492. // GET_DNS_DOMAIN_NAME - return object's DNS domain name.
  1493. // GET_NT4_DOMAIN_NAME - return object's NT4 domain name.
  1494. // GET_FQDN_DOMAIN_NAME - return object's FQDN domain name.
  1495. // GET_OBJ_UPN - return object's User Principal name.
  1496. //
  1497. // Assumptions: Object (pwzNameIn) can be in any domain. Therefore, resolving
  1498. // the name may require chasing referrals.
  1499. // Syntactical-only name cracking is much faster, so don't bind
  1500. // to the DC unless syntactical-only fails.
  1501. //
  1502. // Methodology:
  1503. // GET_OBJ_CAN_NAME - Try syntactical-only first. If that fails, go through
  1504. // the loop again using the DC. Format requested is DS_CANONICAL_NAME.
  1505. // GET_OBJ_CAN_NAME_EX - Same as above except name format requested is
  1506. // DS_CANONICAL_NAME_EX.
  1507. // GET_OBJ_1779_DN - Same as above except name format requested is
  1508. // DS_FQDN_1779_NAME.
  1509. // GET_OBJ_NT4_NAME = Same as above except name format requested is
  1510. // DS_NT4_ACCOUNT_NAME (flat-domain\name).
  1511. // GET_DNS_DOMAIN_NAME - Same as above except return the pDomain element of
  1512. // the DS_NAME_RESULT struct.
  1513. // GET_NT4_DOMAIN_NAME - The NT4 domain name is returned by DsGetDcName.
  1514. // However, we don't know up front whether pwzNameIn is in the current
  1515. // domain or not. Thus, the goal when the loop is first entered will be
  1516. // to obtain a valid DNS domain name. That is, when DsCrackNames returns
  1517. // with a zero status then pwzDomain will contain the DNS domain name for
  1518. // the object's domain. After that, the DomainName value returned by
  1519. // DsGetDcName will be the downlevel domain name.
  1520. // GET_FQDN_DOMAIN_NAME - This is similar to GET_NT4_DOMAIN_NAME except that
  1521. // once the DNS domain name is obtained, DsCrackNames must be called once
  1522. // more to convert that to the 1779 name for the domain.
  1523. //
  1524. //-----------------------------------------------------------------------------
  1525. HRESULT
  1526. CrackName(PWSTR pwzNameIn, PWSTR * ppwzResultName, CRACK_NAME_OPR RequestedOpr,
  1527. HWND hWnd)
  1528. {
  1529. HRESULT hr = S_OK;
  1530. CRACK_NAME_OPR CurOpr = RequestedOpr;
  1531. HANDLE hDS = (HANDLE)-1;
  1532. PDOMAIN_CONTROLLER_INFOW pDCInfo = NULL;
  1533. DWORD dwErr = 0;
  1534. PDS_NAME_RESULTW pDsNameResult = NULL;
  1535. PWSTR * ppwsIn = &pwzNameIn, pwzNameResult = NULL;
  1536. PWSTR pwzDomain = NULL, pwzNT4Domain = NULL, pwzDomRoot = NULL;
  1537. BOOL fLoopAgain = FALSE, fGotDnsDomain = FALSE, fNeedDcBind = FALSE;
  1538. ULONG GetDcFlags = DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED |
  1539. DS_WRITABLE_REQUIRED;
  1540. DS_NAME_FORMAT FormatOffered = DS_FQDN_1779_NAME;
  1541. DS_NAME_FORMAT FormatRequested = DS_CANONICAL_NAME, OriginalFormatRequested = DS_CANONICAL_NAME;
  1542. DS_NAME_FLAGS NameFlags = DS_NAME_FLAG_SYNTACTICAL_ONLY;
  1543. dspDebugOut((DEB_USER1, "CrackName input: %ws\n", pwzNameIn));
  1544. switch (RequestedOpr)
  1545. {
  1546. case GET_NT4_DOMAIN_NAME:
  1547. case GET_FQDN_DOMAIN_NAME:
  1548. CurOpr = GET_DNS_DOMAIN_NAME;
  1549. break;
  1550. case GET_OBJ_1779_DN:
  1551. FormatOffered = DS_UNKNOWN_NAME;
  1552. FormatRequested = DS_FQDN_1779_NAME;
  1553. break;
  1554. case GET_OBJ_NT4_NAME:
  1555. FormatRequested = DS_NT4_ACCOUNT_NAME;
  1556. break;
  1557. case GET_OBJ_CAN_NAME_EX:
  1558. FormatRequested = DS_CANONICAL_NAME_EX;
  1559. break;
  1560. case GET_OBJ_UPN:
  1561. FormatOffered = DS_UNKNOWN_NAME;
  1562. FormatRequested = DS_USER_PRINCIPAL_NAME;
  1563. break;
  1564. }
  1565. do
  1566. {
  1567. if (!(NameFlags & DS_NAME_FLAG_SYNTACTICAL_ONLY))
  1568. {
  1569. // Get a DC name for the domain and bind to it.
  1570. //
  1571. if (CurOpr == GET_NT4_DOMAIN_NAME)
  1572. {
  1573. GetDcFlags |= DS_RETURN_FLAT_NAME;
  1574. }
  1575. dspDebugOut((DEB_ITRACE, "Getting DC for domain '%ws'\n", pwzDomain));
  1576. dwErr = DsGetDcNameW(NULL, pwzDomain, NULL, NULL, GetDcFlags,
  1577. &pDCInfo);
  1578. CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr));
  1579. dspAssert(pDCInfo != NULL);
  1580. if (CurOpr == GET_NT4_DOMAIN_NAME)
  1581. {
  1582. if (FAILED(LocalAllocStringW(&pwzNT4Domain,
  1583. pDCInfo->DomainName)))
  1584. {
  1585. REPORT_ERROR(E_OUTOFMEMORY, hWnd);
  1586. return E_OUTOFMEMORY;
  1587. }
  1588. }
  1589. else
  1590. {
  1591. dwErr = DsBindW(pDCInfo->DomainControllerName, NULL, &hDS);
  1592. }
  1593. #ifdef DSADMIN
  1594. NetApiBufferFree(pDCInfo);
  1595. #else
  1596. LocalFree(pDCInfo);
  1597. #endif
  1598. pDCInfo = NULL;
  1599. if (dwErr != ERROR_SUCCESS)
  1600. {
  1601. // Try again, the DC returned above was unavailable (i.e., the
  1602. // cache list was stale).
  1603. //
  1604. GetDcFlags |= DS_FORCE_REDISCOVERY;
  1605. dwErr = DsGetDcNameW(NULL, pwzDomain, NULL, NULL, GetDcFlags,
  1606. &pDCInfo);
  1607. CHECK_WIN32_REPORT(dwErr, hWnd, ;);
  1608. if (dwErr == ERROR_SUCCESS)
  1609. {
  1610. dspAssert(pDCInfo != NULL);
  1611. if (CurOpr == GET_NT4_DOMAIN_NAME)
  1612. {
  1613. LocalFreeStringW(&pwzNT4Domain);
  1614. if (FAILED(LocalAllocStringW(&pwzNT4Domain,
  1615. pDCInfo->DomainName)))
  1616. {
  1617. REPORT_ERROR(E_OUTOFMEMORY, hWnd);
  1618. return E_OUTOFMEMORY;
  1619. }
  1620. }
  1621. else
  1622. {
  1623. dwErr = DsBindW(pDCInfo->DomainControllerName,
  1624. NULL, &hDS);
  1625. }
  1626. }
  1627. #ifdef DSADMIN
  1628. NetApiBufferFree(pDCInfo);
  1629. #else
  1630. LocalFree(pDCInfo);
  1631. #endif
  1632. }
  1633. CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr));
  1634. if (CurOpr == GET_NT4_DOMAIN_NAME)
  1635. {
  1636. break;
  1637. }
  1638. }
  1639. //
  1640. // Convert the object name.
  1641. //
  1642. dwErr = DsCrackNamesW(hDS, NameFlags, FormatOffered,
  1643. FormatRequested, 1, ppwsIn, &pDsNameResult);
  1644. if (!(NameFlags & DS_NAME_FLAG_SYNTACTICAL_ONLY))
  1645. {
  1646. DsUnBind(&hDS);
  1647. }
  1648. CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr));
  1649. dspAssert(pDsNameResult);
  1650. dspAssert(pDsNameResult->cItems == 1);
  1651. switch (pDsNameResult->rItems->status)
  1652. {
  1653. case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING:
  1654. //
  1655. // A syntactical-only cracking failed, so do it again and use
  1656. // the DC.
  1657. //
  1658. if (fNeedDcBind)
  1659. {
  1660. // Can't syntactically get the domain name, format offered
  1661. // must not be 1779; nothing we can do.
  1662. //
  1663. fLoopAgain = FALSE;
  1664. fNeedDcBind = FALSE;
  1665. pwzNameResult = pwzNameIn;
  1666. hr = MAKE_HRESULT(SEVERITY_ERROR, 0, DS_NAME_ERROR_NO_MAPPING);
  1667. }
  1668. else
  1669. {
  1670. if (DS_FQDN_1779_NAME == FormatOffered)
  1671. {
  1672. // If the format offered is 1779, then a syntactic-only
  1673. // crack for canonical name will supply the DNS domain name.
  1674. // So, set a flag that says we are getting the domain name.
  1675. //
  1676. fNeedDcBind = TRUE;
  1677. OriginalFormatRequested = FormatRequested;
  1678. FormatRequested = DS_CANONICAL_NAME;
  1679. //
  1680. // The DS_NAME_FLAG_PRIVATE_PURE_SYNTACTIC flag means to
  1681. // return the syntactic mapping even for FPOs which other-
  1682. // wise cannot be cracked syntactically.
  1683. //
  1684. NameFlags = (DS_NAME_FLAGS)((int)DS_NAME_FLAG_SYNTACTICAL_ONLY
  1685. | DS_NAME_FLAG_PRIVATE_PURE_SYNTACTIC);
  1686. }
  1687. else
  1688. {
  1689. NameFlags = DS_NAME_NO_FLAGS;
  1690. }
  1691. fLoopAgain = TRUE;
  1692. }
  1693. continue;
  1694. case DS_NAME_ERROR_DOMAIN_ONLY:
  1695. //
  1696. // The object info is in another domain. Go there and try again
  1697. // (unless only the DNS domain name is needed).
  1698. //
  1699. LocalFreeStringW(&pwzDomain);
  1700. if (FAILED(LocalAllocStringW(&pwzDomain,
  1701. pDsNameResult->rItems->pDomain)))
  1702. {
  1703. REPORT_ERROR(E_OUTOFMEMORY, hWnd);
  1704. DsFreeNameResultW(pDsNameResult);
  1705. return E_OUTOFMEMORY;
  1706. }
  1707. DsFreeNameResultW(pDsNameResult);
  1708. pDsNameResult = NULL;
  1709. fGotDnsDomain = TRUE;
  1710. if (RequestedOpr == GET_DNS_DOMAIN_NAME)
  1711. {
  1712. fLoopAgain = FALSE;
  1713. }
  1714. else
  1715. {
  1716. NameFlags = DS_NAME_NO_FLAGS;
  1717. fLoopAgain = TRUE;
  1718. }
  1719. break;
  1720. case DS_NAME_NO_ERROR:
  1721. //
  1722. // Success!
  1723. //
  1724. fLoopAgain = FALSE;
  1725. if (CurOpr == GET_DNS_DOMAIN_NAME || fNeedDcBind)
  1726. {
  1727. fGotDnsDomain = TRUE;
  1728. LocalFreeStringW(&pwzDomain);
  1729. if (FAILED(LocalAllocStringW(&pwzDomain,
  1730. pDsNameResult->rItems->pDomain)))
  1731. {
  1732. REPORT_ERROR(E_OUTOFMEMORY, hWnd);
  1733. DsFreeNameResultW(pDsNameResult);
  1734. return E_OUTOFMEMORY;
  1735. }
  1736. DsFreeNameResultW(pDsNameResult);
  1737. pDsNameResult = NULL;
  1738. if (fNeedDcBind)
  1739. {
  1740. fLoopAgain = TRUE;
  1741. NameFlags = DS_NAME_NO_FLAGS;
  1742. fNeedDcBind = FALSE;
  1743. FormatRequested = OriginalFormatRequested;
  1744. }
  1745. }
  1746. else
  1747. {
  1748. pwzNameResult = pDsNameResult->rItems->pName;
  1749. }
  1750. break;
  1751. case DS_NAME_ERROR_RESOLVING:
  1752. case DS_NAME_ERROR_NOT_FOUND:
  1753. case DS_NAME_ERROR_NO_MAPPING:
  1754. //
  1755. // Can't map the name for some reason, just return the original
  1756. // name and set the return value to DS_NAME_ERROR_NO_MAPPING.
  1757. //
  1758. fLoopAgain = FALSE;
  1759. pwzNameResult = pwzNameIn;
  1760. hr = MAKE_HRESULT(SEVERITY_ERROR, 0, DS_NAME_ERROR_NO_MAPPING);
  1761. break;
  1762. default:
  1763. CHECK_WIN32_REPORT(pDsNameResult->rItems->status, hWnd,
  1764. return MAKE_HRESULT(SEVERITY_ERROR, 0, pDsNameResult->rItems->status));
  1765. break;
  1766. }
  1767. if (RequestedOpr == GET_NT4_DOMAIN_NAME ||
  1768. RequestedOpr == GET_FQDN_DOMAIN_NAME)
  1769. {
  1770. if (CurOpr == GET_FQDN_DOMAIN_NAME)
  1771. {
  1772. DO_DEL(*ppwsIn);
  1773. }
  1774. if (CurOpr == GET_DNS_DOMAIN_NAME)
  1775. {
  1776. if (fGotDnsDomain)
  1777. {
  1778. // Got the DNS domain name, now loop once more to finish.
  1779. //
  1780. dspAssert(pwzDomain);
  1781. fLoopAgain = TRUE;
  1782. CurOpr = RequestedOpr;
  1783. NameFlags = DS_NAME_NO_FLAGS;
  1784. if (RequestedOpr == GET_FQDN_DOMAIN_NAME)
  1785. {
  1786. // JonN 3/3/99; per DaveStr this should be
  1787. FormatOffered = DS_CANONICAL_NAME;
  1788. NameFlags = DS_NAME_FLAG_SYNTACTICAL_ONLY;
  1789. // was FormatOffered = DS_UNKNOWN_NAME;
  1790. FormatRequested = DS_FQDN_1779_NAME;
  1791. pwzDomRoot = new WCHAR[wcslen(pwzDomain) + 2];
  1792. CHECK_NULL_REPORT(pwzDomRoot, hWnd, return E_OUTOFMEMORY);
  1793. wcscpy(pwzDomRoot, pwzDomain);
  1794. wcscat(pwzDomRoot, L"/");
  1795. ppwsIn = &pwzDomRoot;
  1796. }
  1797. }
  1798. else
  1799. {
  1800. fLoopAgain = FALSE;
  1801. }
  1802. }
  1803. }
  1804. } while (fLoopAgain);
  1805. switch (RequestedOpr)
  1806. {
  1807. case GET_OBJ_CAN_NAME:
  1808. case GET_OBJ_CAN_NAME_EX:
  1809. case GET_OBJ_1779_DN:
  1810. case GET_OBJ_NT4_NAME:
  1811. case GET_FQDN_DOMAIN_NAME:
  1812. case GET_OBJ_UPN:
  1813. dspAssert(pDsNameResult);
  1814. if (FAILED(LocalAllocStringW(ppwzResultName, pwzNameResult)))
  1815. {
  1816. DsFreeNameResultW(pDsNameResult);
  1817. REPORT_ERROR(E_OUTOFMEMORY, hWnd);
  1818. return E_OUTOFMEMORY;
  1819. }
  1820. LocalFreeStringW(&pwzDomain);
  1821. DO_DEL(pwzDomRoot);
  1822. break;
  1823. case GET_DNS_DOMAIN_NAME:
  1824. dspAssert(pwzDomain);
  1825. *ppwzResultName = pwzDomain;
  1826. break;
  1827. case GET_NT4_DOMAIN_NAME:
  1828. dspAssert(pwzNT4Domain);
  1829. *ppwzResultName = pwzNT4Domain;
  1830. LocalFreeStringW(&pwzDomain);
  1831. break;
  1832. default:
  1833. dspAssert(FALSE);
  1834. }
  1835. if (pDsNameResult)
  1836. {
  1837. DsFreeNameResultW(pDsNameResult);
  1838. }
  1839. return hr;
  1840. }
  1841. #ifdef DSADMIN
  1842. HRESULT DSPROP_GetGCSearchOnDomain(
  1843. PWSTR pwzDomainDnsName,
  1844. IN REFIID iid,
  1845. OUT void** ppvObject )
  1846. {
  1847. HRESULT hr = S_OK;
  1848. ASSERT( NULL != ppvObject && NULL == *ppvObject );
  1849. CStrW strGC = L"GC:";
  1850. if (pwzDomainDnsName)
  1851. {
  1852. strGC += L"//";
  1853. // strGC += pwzDomainDnsName;
  1854. PDOMAIN_CONTROLLER_INFO pDCInfo;
  1855. DWORD dwErr = DsGetDcName (NULL,
  1856. pwzDomainDnsName,
  1857. NULL,NULL,
  1858. DS_DIRECTORY_SERVICE_REQUIRED,
  1859. &pDCInfo);
  1860. hr = HRESULT_FROM_WIN32(dwErr);
  1861. CHECK_HRESULT(hr, return hr);
  1862. strGC += pDCInfo->DnsForestName;
  1863. NetApiBufferFree(pDCInfo);
  1864. }
  1865. dspDebugOut((DEB_ITRACE, "Binding to %ws\n", strGC));
  1866. hr = ADsOpenObject(const_cast<PWSTR>((LPCWSTR)strGC),
  1867. NULL, NULL, ADS_SECURE_AUTHENTICATION,
  1868. iid, (PVOID *)ppvObject);
  1869. CHECK_HRESULT(hr, return hr);
  1870. return S_OK;
  1871. }
  1872. //+----------------------------------------------------------------------------
  1873. // SMTP address validation. This is adapted from code provided by Eric Dao
  1874. // and Wayne Cranston of the Exchange team.
  1875. //-----------------------------------------------------------------------------
  1876. //+----------------------------------------------------------------------------
  1877. //
  1878. // Function: bParse821IPv4Literal
  1879. //
  1880. // parse: IPv4literal ::= snum 3("." snum)
  1881. // snum ::= one, two or three digits representing a decimal
  1882. // integer in the range 0 through 255
  1883. //
  1884. //-----------------------------------------------------------------------------
  1885. static BOOL bParse821IPv4Literal(PSTR* ppsz)
  1886. {
  1887. PSTR p = *ppsz;
  1888. DWORD n = 0;
  1889. DWORD cOctets = 0;
  1890. DWORD cDigits = 0;
  1891. dspAssert(*p == '[');
  1892. while (*p)
  1893. {
  1894. p++;
  1895. n = 0;
  1896. cDigits = 0;
  1897. if (!isdigit(*p))
  1898. return FALSE;
  1899. while (isdigit(*p))
  1900. {
  1901. if (++cDigits > 3)
  1902. return FALSE;
  1903. n = n * 10 + *p - '0';
  1904. p++;
  1905. }
  1906. if (++cOctets > 4)
  1907. return FALSE;
  1908. if (n > 255)
  1909. return FALSE;
  1910. if ('.' != *p)
  1911. break;
  1912. }
  1913. if (']' != *p || cOctets != 4)
  1914. return FALSE;
  1915. p++;
  1916. *ppsz = p;
  1917. return TRUE;
  1918. }
  1919. //+----------------------------------------------------------------------------
  1920. //
  1921. // Function: bParse821AtDomain
  1922. //
  1923. // parse:
  1924. // <a-d-l> ::= <at-domain> | <at-domain> "," <a-d-l>
  1925. // <at-domain> ::= "@" <domain>
  1926. // <domain> ::= <element> | <element> "." <domain>
  1927. // // IMC does not support #<number> or [127.0.0.1]
  1928. // <element> ::= <name> | "#" <number> | "[" <dotnum> "]"
  1929. // <element> ::= <name>
  1930. // <name> ::= <let-dig> <ldh-str> <let-dig>
  1931. // <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
  1932. // <let-dig> ::= <a> | <d>
  1933. // <let-dig-hyp> ::= <a> | <d> | "-" | "_" ; underscore is invalid, but still used
  1934. // <a> ::= [A-Za-z]
  1935. // <d> ::= [0-9]
  1936. //-----------------------------------------------------------------------------
  1937. static BOOL bParse821AtDomain(PSTR* ppsz)
  1938. {
  1939. PSTR p = *ppsz;
  1940. dspAssert(*p == '@');
  1941. p++;
  1942. if ('\0' == *p)
  1943. return FALSE;
  1944. // Check for IPv4 literals
  1945. //
  1946. if ('[' == *p)
  1947. {
  1948. if (!bParse821IPv4Literal(&p))
  1949. return FALSE;
  1950. goto Exit;
  1951. }
  1952. //
  1953. // Check to be sure the next character is an alpha
  1954. //
  1955. if (!isalnum(*p) && '#' != *p)
  1956. {
  1957. return FALSE;
  1958. }
  1959. p++;
  1960. while (*p)
  1961. {
  1962. if (!(isalnum(*p)))
  1963. return FALSE;
  1964. p++;
  1965. while (*p && (isalnum(*p) || *p == '-'))
  1966. {
  1967. p++;
  1968. }
  1969. if (!isalnum(*(p-1)))
  1970. return FALSE;
  1971. if ('.' != *p)
  1972. break;
  1973. p++;
  1974. }
  1975. Exit:
  1976. *ppsz = p;
  1977. return TRUE;
  1978. }
  1979. //+----------------------------------------------------------------------------
  1980. //
  1981. // Function: bParse821String
  1982. //
  1983. // parse:
  1984. // <string> ::= <char> | <char> <string>
  1985. // <char> ::= <c> | "\" <x>
  1986. // <c> ::= any of the 128 ascii characters, but not any <special>
  1987. // or <sp>
  1988. // <x> ::= any one of the 128 ascii characters (no exceptions)
  1989. // <sp> ::= space (ASCII 32)
  1990. // <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "."
  1991. // | "," | ";" | ":" | "@" | """ | <control>
  1992. // <control> ::= ASCII 0 through ASCII 31 inclusive, and ASCII 127
  1993. //-----------------------------------------------------------------------------
  1994. static BOOL bParse821String(PSTR* ppsz)
  1995. {
  1996. static char rg821Special[] = "<>()[]\\.,;:@\"";
  1997. PSTR p = *ppsz;
  1998. while (*p)
  1999. {
  2000. if ('\\' == *p)
  2001. {
  2002. p++;
  2003. if ('\0' == *p)
  2004. return FALSE;
  2005. }
  2006. else
  2007. {
  2008. if (' ' == *p || strchr(rg821Special, *p) || *p < ' ' || '\x7f' == *p)
  2009. return FALSE;
  2010. }
  2011. p++;
  2012. if ('\0' == *p || '@' == *p || '.' == *p)
  2013. break;
  2014. // Whitespace encountered at this point could be either trailing (if
  2015. // there is no @domain), or it is embedded. We can't really tell the
  2016. // difference (without a lot of work), so fail because there is
  2017. // no other interpretation that is safe.
  2018. //
  2019. if (' ' == *p || '\t' == *p || '>' == *p)
  2020. return FALSE;
  2021. }
  2022. *ppsz = p;
  2023. return TRUE;
  2024. }
  2025. //+----------------------------------------------------------------------------
  2026. //
  2027. // Function: bParse821DotString
  2028. //
  2029. // parse:
  2030. // <dot-string> ::= <string> | <string> "." <string>
  2031. //-----------------------------------------------------------------------------
  2032. static BOOL bParse821DotString(PSTR* ppsz)
  2033. {
  2034. PSTR p = *ppsz;
  2035. while (*p)
  2036. {
  2037. if (!bParse821String(&p))
  2038. return FALSE;
  2039. if ('.' != *p)
  2040. break;
  2041. p++;
  2042. }
  2043. *ppsz = p;
  2044. return TRUE;
  2045. }
  2046. //+----------------------------------------------------------------------------
  2047. //
  2048. // Function: bParse821QuotedString
  2049. //
  2050. // parse:
  2051. // <quoted-string> ::= """ <qtext> """
  2052. // <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>
  2053. // <q> ::= any one of the ascii characters except <cr>, <lf>,
  2054. // quote (") or backslash (\)
  2055. //-----------------------------------------------------------------------------
  2056. static BOOL bParse821QuotedString(PSTR* ppsz)
  2057. {
  2058. PSTR p = *ppsz;
  2059. dspAssert('"' == *p);
  2060. p++;
  2061. while (*p && '"' != *p)
  2062. {
  2063. if ('\\' == *p)
  2064. {
  2065. p++;
  2066. if ('\0' == *p)
  2067. return FALSE;
  2068. }
  2069. else
  2070. {
  2071. if ('\r' == *p || '\n' == *p)
  2072. return FALSE;
  2073. }
  2074. p++;
  2075. }
  2076. if ('"' != *p)
  2077. return FALSE;
  2078. p++;
  2079. *ppsz = p;
  2080. return TRUE;
  2081. }
  2082. //+----------------------------------------------------------------------------
  2083. //
  2084. // Function: bParse821Mailbox
  2085. //
  2086. // parse:
  2087. // <mailbox> ::= <local-part> "@" <domain>
  2088. // <local-part> ::= <dot-string> | <quoted-string>
  2089. //-----------------------------------------------------------------------------
  2090. static BOOL bParse821Mailbox(PSTR* ppsz)
  2091. {
  2092. PSTR p;
  2093. BOOL bSuccess = FALSE;
  2094. dspAssert(ppsz);
  2095. p = *ppsz;
  2096. if ('\0' == *p)
  2097. return FALSE;
  2098. if ('<' == *p)
  2099. {
  2100. p++;
  2101. if ('"' == *p)
  2102. bSuccess = bParse821QuotedString(&p);
  2103. else
  2104. bSuccess = bParse821DotString(&p);
  2105. if (!bSuccess)
  2106. return FALSE;
  2107. if ('@' == *p && !bParse821AtDomain(&p))
  2108. return FALSE;
  2109. if ('>' != *p)
  2110. return FALSE;
  2111. p++;
  2112. }
  2113. else
  2114. {
  2115. if ('"' == *p)
  2116. bSuccess = bParse821QuotedString(&p);
  2117. else
  2118. bSuccess = bParse821DotString(&p);
  2119. if (!bSuccess)
  2120. return FALSE;
  2121. if ('@' == *p && !bParse821AtDomain(&p))
  2122. return FALSE;
  2123. }
  2124. *ppsz = p;
  2125. return TRUE;
  2126. }
  2127. //+----------------------------------------------------------------------------
  2128. //
  2129. // Function: FValidSMTPAddress
  2130. //
  2131. // Synopsis: Valididate mailbox + domain/host name (RFC 821).
  2132. //
  2133. // Return TRUE if we find a valid RFC821 address at pwzBuffer. The UNICODE
  2134. // buffer is converted to ANSI, checked, then converted back. This insures
  2135. // that only ANSI characters are in the buffer when done.
  2136. //
  2137. //-----------------------------------------------------------------------------
  2138. BOOL
  2139. FValidSMTPAddress(PWSTR pwzBuffer)
  2140. {
  2141. dspAssert(pwzBuffer);
  2142. BOOL bUnmappedCharUsed = FALSE;
  2143. int len;
  2144. len = WideCharToMultiByte(CP_ACP, 0, pwzBuffer, -1, NULL, 0, NULL, &bUnmappedCharUsed);
  2145. if (bUnmappedCharUsed)
  2146. {
  2147. return FALSE;
  2148. }
  2149. PSTR pszBuf = new char[len + 1];
  2150. CHECK_NULL_REPORT(pszBuf, GetDesktopWindow(), return FALSE);
  2151. if (WideCharToMultiByte(CP_ACP, 0, pwzBuffer, -1,
  2152. pszBuf, len, NULL, NULL) == 0)
  2153. {
  2154. ReportError(GetLastError(), 0);
  2155. delete [] pszBuf;
  2156. return FALSE;
  2157. }
  2158. PSTR p = pszBuf; // working pointer
  2159. CHECK_NULL_REPORT(p, GetDesktopWindow(), return FALSE);
  2160. if ('\0' == *p)
  2161. {
  2162. delete [] pszBuf;
  2163. return FALSE;
  2164. }
  2165. // Parse routing
  2166. //
  2167. if ('@' == *p)
  2168. {
  2169. while ('@' == *p)
  2170. {
  2171. if (!bParse821AtDomain(&p))
  2172. {
  2173. delete [] pszBuf;
  2174. return FALSE;
  2175. }
  2176. while (' ' == *p || '\t' == *p)
  2177. {
  2178. p++;
  2179. }
  2180. if (',' != *p)
  2181. break;
  2182. p++;
  2183. while (' ' == *p || '\t' == *p)
  2184. {
  2185. p++;
  2186. }
  2187. }
  2188. if (':' != *p)
  2189. {
  2190. delete [] pszBuf;
  2191. return FALSE;
  2192. }
  2193. p++;
  2194. while (' ' == *p || '\t' == *p)
  2195. {
  2196. p++;
  2197. }
  2198. }
  2199. if (!bParse821Mailbox(&p))
  2200. {
  2201. delete [] pszBuf;
  2202. return FALSE;
  2203. }
  2204. // If we are not at end of string, then bParse821Mailbox found an invalid
  2205. // character.
  2206. //
  2207. if ('\0' != *p)
  2208. {
  2209. delete [] pszBuf;
  2210. return FALSE;
  2211. }
  2212. int lenOrig = len;
  2213. len = MultiByteToWideChar(CP_ACP, 0, pszBuf, -1, NULL, 0);
  2214. if (len > lenOrig)
  2215. {
  2216. // Assuming the string won't grow when converted to ANSI and back.
  2217. //
  2218. dspAssert(len <= lenOrig);
  2219. delete [] pszBuf;
  2220. return FALSE;
  2221. }
  2222. if (MultiByteToWideChar(CP_ACP, 0, pszBuf, -1, pwzBuffer, len) == 0)
  2223. {
  2224. ReportError(GetLastError(), 0);
  2225. delete [] pszBuf;
  2226. return FALSE;
  2227. }
  2228. delete [] pszBuf;
  2229. return TRUE;
  2230. }
  2231. #endif // DSADMIN
  2232. void Smart_PADS_ATTR_INFO__Empty( PADS_ATTR_INFO* ppAttrs )
  2233. {
  2234. if (NULL != *ppAttrs)
  2235. {
  2236. FreeADsMem( *ppAttrs );
  2237. *ppAttrs = NULL;
  2238. }
  2239. }