Leaked source code of windows server 2003
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.

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