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.

1526 lines
45 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1999
  6. //
  7. // File: attredit.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #include <SnapBase.h>
  12. #include "resource.h"
  13. #include "common.h"
  14. #include "attredit.h"
  15. #include "connection.h"
  16. #include "attrqry.h"
  17. #ifdef DEBUG_ALLOCATOR
  18. #ifdef _DEBUG
  19. #define new DEBUG_NEW
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23. #endif
  24. ////////////////////////////////////////////////////////////////////////////
  25. // this is used to fill in the attributes for RootDSE
  26. //
  27. typedef struct tagRootDSEAttr
  28. {
  29. LPCWSTR lpszAttr;
  30. LPCWSTR lpszSyntax;
  31. BOOL bMulti;
  32. } SYNTAXMAP;
  33. extern SYNTAXMAP g_ldapRootDSESyntax[];
  34. extern LPCWSTR g_lpszGC;
  35. extern LPCWSTR g_lpszRootDSE;
  36. #ifdef OLD_ATTRIBUTE_CLASS
  37. ///////////////////////////////////////////////////////////////////////////
  38. // CAttrList
  39. // If the property is not found in the attribute list, a NULL position is
  40. // returned.
  41. POSITION CAttrList::FindProperty(LPCWSTR lpszAttr)
  42. {
  43. CADSIAttr* pAttr;
  44. // NOTICE-NTRAID#NTBUG9-556322-2002/05/03-artm Need to validate lpszAttr before using.
  45. // This should never happen, but just in case . . .
  46. if (!lpszAttr)
  47. {
  48. ASSERT(false);
  49. return NULL;
  50. }
  51. for (POSITION p = GetHeadPosition(); p != NULL; GetNext(p))
  52. {
  53. // I use GetAt here because I don't want to advance the POSITION
  54. // because it is returned if they are equal
  55. //
  56. pAttr = GetAt(p);
  57. CString sName;
  58. pAttr->GetProperty(sName);
  59. // NOTICE-2002/02/25-artm sName should be null terminated
  60. // since already in data structure. Validation for lpszAttr
  61. // should be done before entering for loop.
  62. if (wcscmp(sName, lpszAttr) == 0)
  63. {
  64. break;
  65. }
  66. }
  67. return p;
  68. }
  69. BOOL CAttrList::HasProperty(LPCWSTR lpszAttr)
  70. {
  71. POSITION pos = FindProperty(lpszAttr);
  72. return pos != NULL;
  73. }
  74. // Searches through the cache for the attribute
  75. // ppAttr will point to the CADSIAttr if found, NULL if not
  76. //
  77. void CAttrList::GetNextDirty(POSITION& pos, CADSIAttr** ppAttr)
  78. {
  79. *ppAttr = GetNext(pos);
  80. if (pos == NULL && !(*ppAttr)->IsDirty())
  81. {
  82. *ppAttr = NULL;
  83. return;
  84. }
  85. while (!(*ppAttr)->IsDirty() && pos != NULL)
  86. {
  87. *ppAttr = GetNext(pos);
  88. if (!(*ppAttr)->IsDirty() && pos == NULL)
  89. {
  90. *ppAttr = NULL;
  91. break;
  92. }
  93. }
  94. }
  95. BOOL CAttrList::HasDirty()
  96. {
  97. POSITION pos = GetHeadPosition();
  98. while (pos != NULL)
  99. {
  100. CADSIAttr* pAttr = GetNext(pos);
  101. if (pAttr->IsDirty())
  102. {
  103. return TRUE;
  104. }
  105. }
  106. return FALSE;
  107. }
  108. #endif
  109. ///////////////////////////////////////////////////////////////////////////
  110. // CDNSManageButtonTextHelper
  111. CDNSManageButtonTextHelper::CDNSManageButtonTextHelper(int nStates)
  112. {
  113. m_nID = 0;
  114. m_pParentWnd = NULL;
  115. m_nStates = nStates;
  116. m_lpszText = NULL;
  117. m_lpszArr = (LPWSTR*)malloc(sizeof(LPWSTR*)*m_nStates);
  118. if (m_lpszArr != NULL)
  119. {
  120. memset(m_lpszArr, 0x0, sizeof(LPWSTR*)*m_nStates);
  121. }
  122. // FUTURE-2002/02/25-artm If memory allocation fails,
  123. // set the number of states to 0 or maybe even an invalid value.
  124. // That way the caller will be able to check to see if things
  125. // were allocated correctly.
  126. }
  127. CDNSManageButtonTextHelper::~CDNSManageButtonTextHelper()
  128. {
  129. for (int k = 0; k < m_nStates; k++)
  130. {
  131. if (m_lpszArr[k] != NULL)
  132. free(m_lpszArr[k]);
  133. }
  134. free(m_lpszArr);
  135. }
  136. void CDNSManageButtonTextHelper::SetStateX(int nIndex)
  137. {
  138. CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID);
  139. ASSERT(pWnd != NULL);
  140. ASSERT( (nIndex >0) || (nIndex < m_nStates));
  141. pWnd->SetWindowText(m_lpszArr[nIndex]);
  142. }
  143. BOOL CDNSManageButtonTextHelper::Init(CWnd* pParentWnd, UINT nButtonID, UINT* nStrArray)
  144. {
  145. ASSERT(m_pParentWnd == NULL);
  146. ASSERT(pParentWnd != NULL);
  147. m_pParentWnd = pParentWnd;
  148. m_nID = nButtonID;
  149. CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID);
  150. if (pWnd == NULL)
  151. return FALSE;
  152. // get the text for the window
  153. int nSuccessEntries;
  154. // Load the resource strings whose ID's are in nStrArray.
  155. LoadStringArrayFromResource(m_lpszArr, nStrArray, m_nStates, &nSuccessEntries);
  156. ASSERT(nSuccessEntries == m_nStates);
  157. // NTRAID#NTBUG9-554712-2002/02/25-artm No release code backing up assert.
  158. // Release code should validate that the resource strings were all correctly
  159. // loaded.
  160. return TRUE;
  161. }
  162. ///////////////////////////////////////////////////////////////////////////
  163. // CDNSButtonToggleTextHelper
  164. CDNSButtonToggleTextHelper::CDNSButtonToggleTextHelper()
  165. : CDNSManageButtonTextHelper(2)
  166. {
  167. }
  168. ///////////////////////////////////////////////////////////////////////////////
  169. BEGIN_MESSAGE_MAP(CADSIEditBox, CEdit)
  170. ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
  171. END_MESSAGE_MAP()
  172. void CADSIEditBox::OnChange()
  173. {
  174. m_pEditor->OnEditChange();
  175. }
  176. ////////////////////////////////////////////////////////////////
  177. // CADSIValueBox
  178. BEGIN_MESSAGE_MAP(CADSIValueBox, CEdit)
  179. // ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
  180. END_MESSAGE_MAP()
  181. ////////////////////////////////////////////////////////////////
  182. // CADSIValueList
  183. BEGIN_MESSAGE_MAP(CADSIValueList, CListBox)
  184. ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelChange)
  185. END_MESSAGE_MAP()
  186. void CADSIValueList::OnSelChange()
  187. {
  188. m_pEditor->OnValueSelChange();
  189. }
  190. ////////////////////////////////////////////////////////////////
  191. // CADSIAddButton
  192. BEGIN_MESSAGE_MAP(CADSIAddButton, CButton)
  193. ON_CONTROL_REFLECT(BN_CLICKED, OnAdd)
  194. END_MESSAGE_MAP()
  195. void CADSIAddButton::OnAdd()
  196. {
  197. m_pEditor->OnAddValue();
  198. }
  199. ////////////////////////////////////////////////////////////////
  200. // CADSIRemoveButton
  201. BEGIN_MESSAGE_MAP(CADSIRemoveButton, CButton)
  202. ON_CONTROL_REFLECT(BN_CLICKED, OnRemove)
  203. END_MESSAGE_MAP()
  204. void CADSIRemoveButton::OnRemove()
  205. {
  206. m_pEditor->OnRemoveValue();
  207. }
  208. ////////////////////////////////////////////////////////////////
  209. // CAttrEditor
  210. CAttrEditor::CAttrEditor() : m_AttrEditBox(this),
  211. m_SyntaxBox(this),
  212. m_ValueBox(this),
  213. m_ValueList(this),
  214. m_AddButton(this),
  215. m_RemoveButton(this),
  216. m_AddButtonHelper(),
  217. m_RemoveButtonHelper()
  218. {
  219. m_bExisting = TRUE;
  220. m_ptouchedAttr = NULL;
  221. }
  222. BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CTreeNode* pTreeNode,
  223. LPCWSTR lpszServer,
  224. UINT nIDEdit, UINT nIDSyntax,
  225. UINT nIDValueBox, UINT nIDValueList,
  226. UINT nIDAddButton, UINT nIDRemoveButton,
  227. BOOL bComplete)
  228. {
  229. ASSERT(pParentWnd != NULL);
  230. if (pParentWnd == NULL)
  231. return FALSE;
  232. m_pParentWnd = pParentWnd;
  233. m_ptouchedAttr = new CAttrList();
  234. ASSERT(m_ptouchedAttr != NULL);
  235. if (pTreeNode == NULL)
  236. {
  237. m_bExisting = FALSE;
  238. }
  239. else
  240. {
  241. m_bExisting = TRUE;
  242. }
  243. m_sServer = lpszServer;
  244. if (m_bExisting)
  245. {
  246. // This gets the CConnectionData from the ConnectionNode by finding a valid treenode and using its
  247. // CADsObject to get the ConnectionNode and then the CConnectionData
  248. //
  249. m_pTreeNode = pTreeNode;
  250. CADSIEditContainerNode* pContNode = dynamic_cast<CADSIEditContainerNode*>(m_pTreeNode);
  251. if (pContNode == NULL)
  252. {
  253. CADSIEditLeafNode* pLeafNode = dynamic_cast<CADSIEditLeafNode*>(m_pTreeNode);
  254. ASSERT(pLeafNode != NULL);
  255. m_pConnectData = pLeafNode->GetADsObject()->GetConnectionNode()->GetConnectionData();
  256. }
  257. else
  258. {
  259. m_pConnectData = pContNode->GetADsObject()->GetConnectionNode()->GetConnectionData();
  260. }
  261. }
  262. // sublclass controls
  263. //
  264. BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd);
  265. ASSERT(bRes);
  266. if (!bRes) return FALSE;
  267. bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd);
  268. ASSERT(bRes);
  269. if (!bRes) return FALSE;
  270. bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd);
  271. ASSERT(bRes);
  272. if (!bRes) return FALSE;
  273. bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd);
  274. ASSERT(bRes);
  275. if (!bRes) return FALSE;
  276. bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd);
  277. ASSERT(bRes);
  278. if (!bRes) return FALSE;
  279. UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET };
  280. m_AddButtonHelper.Init(m_pParentWnd,
  281. nIDAddButton,
  282. nAddButtonTextIDs);
  283. bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd);
  284. ASSERT(bRes);
  285. if (!bRes) return FALSE;
  286. UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR };
  287. m_RemoveButtonHelper.Init(m_pParentWnd,
  288. nIDRemoveButton,
  289. nRemoveButtonTextIDs);
  290. // NOTICE-2002/02/27-artm Under low memory environment CString can
  291. // throw out of memory exceptions. ADSI edit prefers to shutdown over
  292. // trying to handle the exception (since no other components depend on
  293. // the tool).
  294. if (!m_sNotSet.LoadString(IDS_NOT_SET))
  295. {
  296. return FALSE;
  297. }
  298. if ( bComplete)
  299. {
  300. // Show property values as single and without the ability to set or clear
  301. //
  302. SetPropertyUI(0, FALSE, TRUE);
  303. }
  304. else
  305. {
  306. m_AttrEditBox.ShowWindow(SW_HIDE);
  307. m_SyntaxBox.ShowWindow(SW_HIDE);
  308. m_ValueBox.ShowWindow(SW_HIDE);
  309. m_ValueList.ShowWindow(SW_HIDE);
  310. m_AddButton.ShowWindow(SW_HIDE);
  311. m_RemoveButton.ShowWindow(SW_HIDE);
  312. }
  313. return bRes;
  314. }
  315. BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CConnectionData* pConnectData,
  316. LPCWSTR lpszServer,
  317. UINT nIDEdit, UINT nIDSyntax,
  318. UINT nIDValueBox, UINT nIDValueList,
  319. UINT nIDAddButton, UINT nIDRemoveButton,
  320. BOOL bComplete, CAttrList* pAttrList)
  321. {
  322. ASSERT(pParentWnd != NULL);
  323. if (pParentWnd == NULL)
  324. return FALSE;
  325. m_pParentWnd = pParentWnd;
  326. m_bExisting = FALSE;
  327. m_sServer = lpszServer;
  328. m_pConnectData = pConnectData;
  329. ASSERT(pAttrList != NULL);
  330. m_ptouchedAttr = pAttrList;
  331. // sublclass controls
  332. //
  333. BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd);
  334. ASSERT(bRes);
  335. if (!bRes) return FALSE;
  336. bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd);
  337. ASSERT(bRes);
  338. if (!bRes) return FALSE;
  339. bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd);
  340. ASSERT(bRes);
  341. if (!bRes) return FALSE;
  342. bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd);
  343. ASSERT(bRes);
  344. if (!bRes) return FALSE;
  345. bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd);
  346. ASSERT(bRes);
  347. if (!bRes) return FALSE;
  348. UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET };
  349. m_AddButtonHelper.Init(m_pParentWnd,
  350. nIDAddButton,
  351. nAddButtonTextIDs);
  352. bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd);
  353. ASSERT(bRes);
  354. if (!bRes) return FALSE;
  355. UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR };
  356. m_RemoveButtonHelper.Init(m_pParentWnd,
  357. nIDRemoveButton,
  358. nRemoveButtonTextIDs);
  359. // NOTICE-2002/02/27-artm Under low memory environment CString can
  360. // throw out of memory exceptions. ADSI edit prefers to shutdown over
  361. // trying to handle the exception (since no other components depend on
  362. // the tool).
  363. if (!m_sNotSet.LoadString(IDS_NOT_SET))
  364. {
  365. return FALSE;
  366. }
  367. if ( bComplete)
  368. {
  369. // Show property values as single and without the ability to set or clear
  370. //
  371. SetPropertyUI(0, FALSE, TRUE);
  372. }
  373. else
  374. {
  375. m_AttrEditBox.ShowWindow(SW_HIDE);
  376. m_SyntaxBox.ShowWindow(SW_HIDE);
  377. m_ValueBox.ShowWindow(SW_HIDE);
  378. m_ValueList.ShowWindow(SW_HIDE);
  379. m_AddButton.ShowWindow(SW_HIDE);
  380. m_RemoveButton.ShowWindow(SW_HIDE);
  381. }
  382. return bRes;
  383. }
  384. void CAttrEditor::SetAttribute(LPCWSTR lpszAttr, LPCWSTR lpszPath)
  385. {
  386. m_sAttr = lpszAttr;
  387. m_sPath = lpszPath;
  388. DisplayAttribute();
  389. }
  390. BOOL CAttrEditor::OnApply()
  391. {
  392. if (m_bExisting && m_ptouchedAttr->HasDirty() &&
  393. !m_pConnectData->IsRootDSE() &&
  394. !m_pConnectData->IsGC())
  395. {
  396. CComPtr<IDirectoryObject> pDirObject;
  397. // bind to object with authentication
  398. //
  399. HRESULT hr, hCredResult;
  400. hr = OpenObjectWithCredentials(
  401. m_pConnectData,
  402. m_pConnectData->GetCredentialObject()->UseCredentials(),
  403. m_sPath,
  404. IID_IDirectoryObject,
  405. (LPVOID*) &pDirObject,
  406. NULL,
  407. hCredResult
  408. );
  409. if (FAILED(hr))
  410. {
  411. if (SUCCEEDED(hCredResult))
  412. {
  413. ADSIEditErrorMessage(hr);
  414. m_pParentWnd->SetModified(FALSE);
  415. }
  416. // Need to change the focus or we will not be able to navigate with the keyboard
  417. m_AttrEditBox.SetFocus();
  418. return FALSE;
  419. }
  420. // Change or add values to ADSI cache that have changed
  421. //
  422. hr = CADSIAttr::SetValuesInDS(m_ptouchedAttr, pDirObject);
  423. if (FAILED(hr))
  424. {
  425. //Format Error message and pop up a dialog
  426. ADSIEditErrorMessage(hr);
  427. m_ptouchedAttr->RemoveAllAttr();
  428. DisplayAttribute();
  429. m_pParentWnd->SetModified(FALSE);
  430. // Need to change the focus or we will not be able to navigate with the keyboard
  431. m_AttrEditBox.SetFocus();
  432. return FALSE;
  433. }
  434. }
  435. m_pParentWnd->SetModified(FALSE);
  436. return TRUE;
  437. }
  438. void CAttrEditor::OnAddValue()
  439. {
  440. ASSERT(!m_pConnectData->IsRootDSE());
  441. ASSERT(!m_pConnectData->IsGC());
  442. // NOTICE-2002/02/25-artm Only threat here is that CString will throw out of memory.
  443. CString s;
  444. m_AttrEditBox.GetWindowText(s);
  445. CStringList sList;
  446. m_pAttr->GetValues(sList);
  447. if (m_pAttr->GetMultivalued())
  448. {
  449. // if it is the first value to be added we need to get rid of the "<not set>"
  450. //
  451. CString sNotSet;
  452. m_ValueList.GetText(0, sNotSet);
  453. if (sNotSet == m_sNotSet)
  454. {
  455. m_ValueList.ResetContent();
  456. }
  457. // then add the new value
  458. //
  459. sList.AddTail(s);
  460. }
  461. else
  462. {
  463. // since it is single valued, remove the old one and add the new one
  464. //
  465. sList.RemoveAll();
  466. sList.AddTail(s);
  467. }
  468. HRESULT hr = m_pAttr->SetValues(sList);
  469. if (FAILED(hr))
  470. {
  471. DisplayFormatError();
  472. }
  473. else
  474. {
  475. if ( m_pAttr->GetMultivalued())
  476. {
  477. m_ValueList.AddString(s);
  478. }
  479. else
  480. {
  481. m_ValueBox.SetWindowText(s);
  482. }
  483. m_AttrEditBox.SetWindowText(_T(""));
  484. m_pAttr->SetDirty(TRUE);
  485. m_pParentWnd->SetModified(TRUE);
  486. // Make the UI reflect the new data
  487. //
  488. m_AttrEditBox.SetFocus();
  489. SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE);
  490. // Enable the clear button if the attribute is not multivalued
  491. //
  492. if ( !m_pAttr->GetMultivalued())
  493. {
  494. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  495. }
  496. }
  497. }
  498. void CAttrEditor::DisplayFormatError()
  499. {
  500. switch (m_pAttr->GetADsType())
  501. {
  502. case ADSTYPE_DN_STRING :
  503. case ADSTYPE_CASE_EXACT_STRING :
  504. case ADSTYPE_CASE_IGNORE_STRING :
  505. case ADSTYPE_PRINTABLE_STRING :
  506. case ADSTYPE_NUMERIC_STRING :
  507. {
  508. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  509. if (m_pAttr->GetMultivalued())
  510. {
  511. if (m_ValueList.GetCount() < 1)
  512. {
  513. m_ValueList.AddString(m_sNotSet);
  514. }
  515. }
  516. break;
  517. }
  518. case ADSTYPE_BOOLEAN :
  519. {
  520. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_BOOLEAN, MB_OK);
  521. if (m_pAttr->GetMultivalued())
  522. {
  523. if (m_ValueList.GetCount() < 1)
  524. {
  525. m_ValueList.AddString(m_sNotSet);
  526. }
  527. }
  528. break;
  529. }
  530. case ADSTYPE_INTEGER :
  531. {
  532. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  533. if (m_pAttr->GetMultivalued())
  534. {
  535. if (m_ValueList.GetCount() < 1)
  536. {
  537. m_ValueList.AddString(m_sNotSet);
  538. }
  539. }
  540. break;
  541. }
  542. case ADSTYPE_OCTET_STRING :
  543. {
  544. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_OCTET, MB_OK);
  545. if (m_pAttr->GetMultivalued())
  546. {
  547. if (m_ValueList.GetCount() < 1)
  548. {
  549. m_ValueList.AddString(m_sNotSet);
  550. }
  551. }
  552. break;
  553. }
  554. case ADSTYPE_UTC_TIME :
  555. {
  556. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_TIME, MB_OK);
  557. if (m_pAttr->GetMultivalued())
  558. {
  559. if (m_ValueList.GetCount() < 1)
  560. {
  561. m_ValueList.AddString(m_sNotSet);
  562. }
  563. }
  564. break;
  565. }
  566. case ADSTYPE_LARGE_INTEGER :
  567. case ADSTYPE_OBJECT_CLASS :
  568. case ADSTYPE_UNKNOWN :
  569. {
  570. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  571. if (m_pAttr->GetMultivalued())
  572. {
  573. if (m_ValueList.GetCount() < 1)
  574. {
  575. m_ValueList.AddString(m_sNotSet);
  576. }
  577. }
  578. break;
  579. }
  580. default :
  581. {
  582. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  583. if (m_pAttr->GetMultivalued())
  584. {
  585. if (m_ValueList.GetCount() < 1)
  586. {
  587. m_ValueList.AddString(m_sNotSet);
  588. }
  589. }
  590. break;
  591. }
  592. }
  593. }
  594. void CAttrEditor::OnRemoveValue()
  595. {
  596. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  597. {
  598. CStringList sList;
  599. m_pAttr->GetValues(sList);
  600. DWORD dwNumVals = m_pAttr->GetNumValues();
  601. if (m_pAttr->GetMultivalued())
  602. {
  603. CString s, sVal;
  604. int iCount = m_ValueList.GetCurSel();
  605. m_ValueList.GetText(iCount, sVal);
  606. m_AttrEditBox.SetWindowText(sVal);
  607. m_ValueList.DeleteString(iCount);
  608. // Add "<not set>" to the UI if this is the last value being removed
  609. //
  610. if (m_ValueList.GetCount() == 0)
  611. {
  612. m_AttrEditBox.SetFocus();
  613. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  614. m_ValueList.AddString(m_sNotSet);
  615. if (!m_bExisting)
  616. {
  617. m_pAttr->SetDirty(FALSE);
  618. }
  619. }
  620. POSITION pos = sList.FindIndex(iCount);
  621. sList.RemoveAt(pos);
  622. HRESULT hr = m_pAttr->SetValues(sList);
  623. if (FAILED(hr))
  624. {
  625. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  626. }
  627. if (m_bExisting || m_ValueList.GetCount() > 0)
  628. {
  629. m_pAttr->SetDirty(TRUE);
  630. }
  631. }
  632. else
  633. {
  634. // NOTICE-2002/02/25-artm Only threat is that CString will throw
  635. // out of memory exception. The input is as safe as can be, coming
  636. // from UI and there is no manipulation.
  637. CString sVal;
  638. m_ValueBox.GetWindowText(sVal);
  639. m_AttrEditBox.SetWindowText(sVal);
  640. m_ValueBox.SetWindowText( m_sNotSet);
  641. sList.RemoveAll();
  642. HRESULT hr = m_pAttr->SetValues(sList);
  643. if (FAILED(hr))
  644. {
  645. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  646. }
  647. if (!m_bExisting)
  648. {
  649. m_pAttr->SetDirty(FALSE);
  650. }
  651. else
  652. {
  653. m_pAttr->SetDirty(TRUE);
  654. }
  655. }
  656. m_AttrEditBox.SetFocus();
  657. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  658. dwNumVals--;
  659. m_pParentWnd->SetModified(TRUE);
  660. }
  661. }
  662. void CAttrEditor::OnEditChange()
  663. {
  664. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  665. {
  666. CString s;
  667. // NOTICE-2002/02/25-artm Only threat is that CString can throw
  668. // out of memory exceptions.
  669. m_AttrEditBox.GetWindowText(s);
  670. if (s != _T(""))
  671. {
  672. SetPropertyUI(TN_FLAG_ENABLE_ADD, FALSE);
  673. }
  674. else
  675. {
  676. SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE);
  677. }
  678. }
  679. }
  680. void CAttrEditor::OnValueSelChange()
  681. {
  682. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  683. {
  684. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  685. }
  686. }
  687. void CAttrEditor::GetAttrFailed()
  688. {
  689. CString sSyntax;
  690. GetSyntax(m_sAttr, sSyntax);
  691. m_SyntaxBox.SetWindowText(sSyntax);
  692. m_ValueList.ResetContent();
  693. m_ValueList.AddString(m_sNotSet);
  694. m_ValueBox.SetWindowText(m_sNotSet);
  695. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  696. }
  697. void CAttrEditor::FillWithExisting()
  698. {
  699. CString s;
  700. m_pAttr = m_ptouchedAttr->GetAt(m_ptouchedAttr->FindProperty(m_sAttr));
  701. ASSERT(m_pAttr != NULL);
  702. CStringList slValues;
  703. m_pAttr->GetValues(slValues);
  704. if (m_pAttr->GetMultivalued())
  705. {
  706. m_ValueList.ResetContent();
  707. POSITION pos;
  708. if (slValues.GetCount() == 0)
  709. {
  710. slValues.AddTail(m_sNotSet);
  711. }
  712. for (pos = slValues.GetHeadPosition(); pos != NULL; slValues.GetNext(pos) )
  713. {
  714. s = slValues.GetAt(pos);
  715. m_ValueList.AddString(s);
  716. }
  717. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  718. }
  719. else
  720. {
  721. if (slValues.GetCount() > 0)
  722. {
  723. s = slValues.GetAt(slValues.GetHeadPosition());
  724. m_ValueBox.SetWindowText(s);
  725. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  726. {
  727. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  728. }
  729. }
  730. else
  731. {
  732. m_ValueBox.SetWindowText(m_sNotSet);
  733. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  734. }
  735. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  736. }
  737. }
  738. void CAttrEditor::DisplayAttribute()
  739. {
  740. int iCount;
  741. HRESULT hr, hCredResult;
  742. if (m_ptouchedAttr->HasProperty(m_sAttr))
  743. {
  744. FillWithExisting();
  745. }
  746. else
  747. {
  748. if (m_pConnectData->IsRootDSE())
  749. {
  750. DisplayRootDSE();
  751. }
  752. else if (!m_bExisting)
  753. {
  754. ADS_ATTR_INFO *pAttrInfo = NULL;
  755. GetAttrFailed();
  756. m_pAttr = TouchAttr(m_sAttr);
  757. ASSERT(m_pAttr != NULL);
  758. if (m_pAttr != NULL)
  759. {
  760. if (m_pAttr->GetMultivalued())
  761. {
  762. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  763. }
  764. else
  765. {
  766. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  767. }
  768. }
  769. return;
  770. }
  771. else
  772. {
  773. CComPtr<IDirectoryObject> pDirObject;
  774. hr = OpenObjectWithCredentials(
  775. m_pConnectData,
  776. m_pConnectData->GetCredentialObject()->UseCredentials(),
  777. m_sPath,
  778. IID_IDirectoryObject,
  779. (LPVOID*) &pDirObject,
  780. NULL,
  781. hCredResult
  782. );
  783. if ( FAILED(hr) )
  784. {
  785. if (SUCCEEDED(hCredResult))
  786. {
  787. ADSIEditErrorMessage(hr);
  788. }
  789. return;
  790. }
  791. ASSERT(pDirObject != NULL);
  792. // Get attribute
  793. //
  794. CString szAttrName;
  795. szAttrName = m_sAttr + _T(";range=0-*");
  796. CString szFormat = m_sAttr + _T(";range=%ld-*");
  797. DWORD dwReturn = 0;
  798. DWORD dwNumAttr = 1;
  799. ADS_ATTR_INFO *pAttrInfo;
  800. const WCHAR wcSep = L'-';
  801. const WCHAR wcEnd = L'*';
  802. BOOL fMoreRemain = FALSE;
  803. CStringList sList;
  804. do
  805. {
  806. LPWSTR lpszAttrs[] = {(LPWSTR)(LPCWSTR)szAttrName};
  807. hr = pDirObject->GetObjectAttributes(lpszAttrs, dwNumAttr, &pAttrInfo, &dwReturn);
  808. if (FAILED(hr))
  809. {
  810. ADSIEditErrorMessage(hr);
  811. return;
  812. }
  813. if (pAttrInfo == NULL)
  814. {
  815. GetAttrFailed();
  816. m_pAttr = TouchAttr(m_sAttr);
  817. ASSERT(m_pAttr != NULL);
  818. if (m_pAttr != NULL)
  819. {
  820. if (m_pAttr->GetMultivalued())
  821. {
  822. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  823. }
  824. else
  825. {
  826. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  827. }
  828. }
  829. return;
  830. }
  831. if (dwReturn > 0)
  832. {
  833. GetStringFromADs(pAttrInfo, sList);
  834. }
  835. //
  836. // Check to see if there is more data. If the last char of the
  837. // attribute name string is an asterisk, then we have everything.
  838. //
  839. // NOTICE-2002/02/25-artm This is trusting that AD is null
  840. // terminating the name of the attribute. The reasons why this
  841. // trust in service outside the component is allowable are:
  842. // 1) If AD is being spoofed the attacker will 'only' get us to read
  843. // bogus memory. This will AV the client app.
  844. // 2) There is no way to validate the length of the attribute name
  845. // string with the current ADS_ATTR_INFO structure.
  846. int cchEnd = wcslen(pAttrInfo->pszAttrName);
  847. fMoreRemain = pAttrInfo->pszAttrName[cchEnd - 1] != wcEnd;
  848. if (fMoreRemain)
  849. {
  850. PWSTR pwz = wcsrchr(pAttrInfo->pszAttrName, wcSep);
  851. if (!pwz)
  852. {
  853. ASSERT(FALSE && pAttrInfo->pszAttrName);
  854. fMoreRemain = FALSE;
  855. }
  856. else
  857. {
  858. pwz++; // move past the hyphen to the range end value.
  859. // NOTICE-2002/02/27-artm Assert does not require release code.
  860. // The assert is a sanity check that the ADSI interface did not return an
  861. // undocumented string format. In other words, there should never be a time
  862. // when this branch is entered and the '-' is the last character in the string.
  863. // The release code does not need this check b/c if it does occur (and it shouldn't),
  864. // there's a bug in ADSI. The tool will crash from reading past pointer and go
  865. // into Dr. Watson (which can be used just as well to track down the bug).
  866. ASSERT(*pwz);
  867. long lEnd = _wtol(pwz);
  868. lEnd++; // start with the next value.
  869. szAttrName.Format(szFormat, lEnd);
  870. TRACE(L"Range returned is %ws, now asking for %ws\n",
  871. pAttrInfo->pszAttrName, szAttrName);
  872. }
  873. }
  874. } while (fMoreRemain);
  875. BOOL bMulti = FALSE;
  876. if (m_pConnectData->IsGC())
  877. {
  878. bMulti = IsMultiValued(pAttrInfo);
  879. }
  880. else
  881. {
  882. bMulti = IsMultiValued(m_sAttr);
  883. }
  884. if (pAttrInfo != NULL)
  885. {
  886. if (bMulti)
  887. {
  888. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  889. m_ValueList.ResetContent();
  890. POSITION pos = sList.GetHeadPosition();
  891. while (pos != NULL)
  892. {
  893. CString sValue = sList.GetNext(pos);
  894. m_ValueList.AddString(sValue);
  895. }
  896. }
  897. else
  898. {
  899. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  900. if (sList.GetCount() > 0)
  901. {
  902. m_ValueBox.SetWindowText(sList.GetHead());
  903. }
  904. if (!m_pConnectData->IsGC())
  905. {
  906. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  907. }
  908. }
  909. }
  910. else
  911. {
  912. GetAttrFailed();
  913. CStringList sTempList;
  914. m_pAttr = TouchAttr(pAttrInfo, bMulti);
  915. if (bMulti)
  916. {
  917. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  918. }
  919. else
  920. {
  921. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  922. }
  923. return;
  924. }
  925. m_pAttr = TouchAttr(pAttrInfo, bMulti);
  926. }
  927. }
  928. CString sSyntax;
  929. GetSyntax(m_sAttr, sSyntax);
  930. m_SyntaxBox.SetWindowText(sSyntax);
  931. }
  932. void CAttrEditor::DisplayRootDSE()
  933. {
  934. CString s = m_sPath;
  935. CComPtr<IADs> pADs;
  936. HRESULT hr, hCredResult;
  937. hr = OpenObjectWithCredentials(
  938. m_pConnectData,
  939. m_pConnectData->GetCredentialObject()->UseCredentials(),
  940. s,
  941. IID_IADs,
  942. (LPVOID*)&pADs,
  943. NULL,
  944. hCredResult
  945. );
  946. if ( FAILED(hr) )
  947. {
  948. if (SUCCEEDED(hCredResult))
  949. {
  950. ADSIEditErrorMessage(hr);
  951. }
  952. return;
  953. }
  954. // This is to insure that the ADSI cache is current
  955. //
  956. hr = pADs->GetInfo();
  957. VARIANT var;
  958. hr = pADs->GetEx( CComBSTR(m_sAttr) , &var );
  959. if ( FAILED(hr) )
  960. {
  961. GetAttrFailed();
  962. m_pAttr = TouchAttr(m_sAttr);
  963. return;
  964. }
  965. /////////////////////////////////////////
  966. // Convert and populate
  967. ///////////////////////////////////////////
  968. CStringList sList;
  969. hr = VariantToStringList( var, sList );
  970. if ( FAILED(hr) )
  971. {
  972. GetAttrFailed();
  973. VariantClear(&var);
  974. CStringList sTempList;
  975. m_pAttr = TouchAttr(m_sAttr);
  976. return;
  977. }
  978. VariantClear( &var );
  979. if ( IsRootDSEAttrMultiValued(m_sAttr) )
  980. {
  981. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE);
  982. m_ValueList.ResetContent();
  983. POSITION pos = sList.GetHeadPosition();
  984. while (pos != NULL)
  985. {
  986. CString sValue = sList.GetNext(pos);
  987. m_ValueList.AddString(sValue);
  988. }
  989. }
  990. else
  991. {
  992. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  993. s = sList.GetHead();
  994. m_ValueBox.SetWindowText(s);
  995. }
  996. // m_pAttr = TouchAttr(m_sAttr);
  997. CString sSyntax;
  998. GetSyntax(m_sAttr, sSyntax);
  999. m_SyntaxBox.SetWindowText(sSyntax);
  1000. // REVEIW : this is the only occurrance of "UTCTime", if there
  1001. // becomes more we may need to make a global string or something
  1002. //
  1003. if (sSyntax == _T("UTCTime"))
  1004. {
  1005. CString sFormatted, sRemainder;
  1006. CString sYear, sMonth, sDay, sHour, sMinute, sSecond;
  1007. int iCount = 0;
  1008. sYear = s.Left(4);
  1009. iCount = s.GetLength();
  1010. sRemainder = s.Right(iCount - 4);
  1011. sMonth = sRemainder.Left(2);
  1012. iCount = sRemainder.GetLength();
  1013. sRemainder = sRemainder.Right(iCount - 2);
  1014. sDay = sRemainder.Left(2);
  1015. iCount = sRemainder.GetLength();
  1016. sRemainder = sRemainder.Right(iCount - 2);
  1017. sHour = sRemainder.Left(2);
  1018. iCount = sRemainder.GetLength();
  1019. sRemainder = sRemainder.Right(iCount - 2);
  1020. sMinute = sRemainder.Left(2);
  1021. iCount = sRemainder.GetLength();
  1022. sRemainder = sRemainder.Right(iCount - 2);
  1023. sSecond = sRemainder.Left(2);
  1024. sFormatted = sMonth + _T("/") + sDay + _T("/") + sYear + _T(" ")
  1025. + sHour + _T(":") + sMinute + _T(":") + sSecond;
  1026. m_ValueBox.SetWindowText(sFormatted);
  1027. }
  1028. }
  1029. // Pre: lpszAttr non-NULL and must be null terminated
  1030. BOOL CAttrEditor::IsRootDSEAttrMultiValued(LPCWSTR lpszAttr)
  1031. {
  1032. int idx=0, iCount = 0;
  1033. // NOTICE-2002/02/26-artm This method only called from SetAttribute()
  1034. // and is not a public function. If it were public we would need to be
  1035. // careful about a NULL lpszAttr and potentially a string that is not
  1036. // null terminated.
  1037. iCount = wcslen(lpszAttr);
  1038. while( g_ldapRootDSESyntax[idx].lpszAttr)
  1039. {
  1040. // NOTICE-2002/02/26-artm Comparison well bounded by length
  1041. // of lpszAttr.
  1042. if ( _wcsnicmp(g_ldapRootDSESyntax[idx].lpszAttr, lpszAttr, iCount) == 0)
  1043. {
  1044. return g_ldapRootDSESyntax[idx].bMulti;
  1045. }
  1046. idx++;
  1047. }
  1048. return FALSE;
  1049. }
  1050. // TODO : This is extremely ugly, redo it
  1051. //
  1052. void CAttrEditor::SetPropertyUI(DWORD dwFlags, BOOL bAnd, BOOL bReset)
  1053. {
  1054. if (bReset)
  1055. {
  1056. m_dwMultiFlags = dwFlags;
  1057. }
  1058. if (bAnd)
  1059. {
  1060. m_dwMultiFlags &= dwFlags;
  1061. }
  1062. else
  1063. {
  1064. m_dwMultiFlags |= dwFlags;
  1065. }
  1066. if (m_dwMultiFlags & TN_FLAG_SHOW_MULTI)
  1067. {
  1068. m_AddButtonHelper.SetToggleState(TRUE);
  1069. m_RemoveButtonHelper.SetToggleState(TRUE);
  1070. m_ValueList.ShowWindow(SW_SHOW);
  1071. m_ValueBox.ShowWindow(SW_HIDE);
  1072. }
  1073. else
  1074. {
  1075. m_AddButtonHelper.SetToggleState(FALSE);
  1076. m_RemoveButtonHelper.SetToggleState(FALSE);
  1077. m_ValueList.ShowWindow(SW_HIDE);
  1078. m_ValueBox.ShowWindow(SW_SHOW);
  1079. }
  1080. if (m_dwMultiFlags & TN_FLAG_ENABLE_REMOVE)
  1081. {
  1082. m_RemoveButton.EnableWindow(TRUE);
  1083. }
  1084. else
  1085. {
  1086. m_RemoveButton.EnableWindow(FALSE);
  1087. }
  1088. if (m_dwMultiFlags & TN_FLAG_ENABLE_ADD)
  1089. {
  1090. m_AddButton.EnableWindow(TRUE);
  1091. }
  1092. else
  1093. {
  1094. m_AddButton.EnableWindow(FALSE);
  1095. }
  1096. if (m_bExisting && (m_pConnectData->IsGC() || m_pConnectData->IsRootDSE()))
  1097. {
  1098. m_AttrEditBox.EnableWindow(FALSE);
  1099. }
  1100. else
  1101. {
  1102. m_AttrEditBox.EnableWindow(TRUE);
  1103. }
  1104. }
  1105. // Pre: lpszProp non-NULL and null terminated string
  1106. void CAttrEditor::GetSyntax(LPCWSTR lpszProp, CString& sSyntax)
  1107. {
  1108. if (m_bExisting && m_pConnectData->IsRootDSE())
  1109. {
  1110. int idx=0;
  1111. while( g_ldapRootDSESyntax[idx].lpszAttr )
  1112. {
  1113. // NOTICE-2002/02/26-artm Use of wcscmp() is ok b/c this is
  1114. // a protected function. If it were public we would need to
  1115. // worry about a NULL pointer and strings that weren't null
  1116. // terminated.
  1117. if ( wcscmp(lpszProp, g_ldapRootDSESyntax[idx].lpszAttr) == 0 )
  1118. {
  1119. sSyntax = g_ldapRootDSESyntax[idx].lpszSyntax;
  1120. return;
  1121. }
  1122. idx++;
  1123. }
  1124. }
  1125. else
  1126. {
  1127. CComPtr<IADsProperty> pProp;
  1128. HRESULT hr, hCredResult;
  1129. CString schema;
  1130. m_pConnectData->GetAbstractSchemaPath(schema);
  1131. schema = schema + lpszProp;
  1132. hr = OpenObjectWithCredentials(
  1133. m_pConnectData,
  1134. m_pConnectData->GetCredentialObject()->UseCredentials(),
  1135. schema,
  1136. IID_IADsProperty,
  1137. (LPVOID*) &pProp,
  1138. NULL,
  1139. hCredResult
  1140. );
  1141. if ( FAILED(hr) )
  1142. {
  1143. if (SUCCEEDED(hCredResult))
  1144. {
  1145. ADSIEditErrorMessage(hr);
  1146. }
  1147. return;
  1148. }
  1149. ///////////////////////////////////////////////////
  1150. // Create a new cached attribute and populate
  1151. //////////////////////////////////////////////////
  1152. BSTR bstr;
  1153. hr = pProp->get_Syntax( &bstr );
  1154. if ( SUCCEEDED(hr) )
  1155. {
  1156. sSyntax = bstr;
  1157. }
  1158. SysFreeString(bstr);
  1159. }
  1160. }
  1161. BOOL CAttrEditor::IsMultiValued(ADS_ATTR_INFO* pAttrInfo)
  1162. {
  1163. return (pAttrInfo->dwNumValues > 1) ? TRUE : FALSE;
  1164. }
  1165. BOOL CAttrEditor::IsMultiValued(LPCWSTR lpszProp)
  1166. {
  1167. CString schema;
  1168. BOOL bResult = FALSE;
  1169. CADSIEditContainerNode* pContNode = dynamic_cast<CADSIEditContainerNode*>(m_pTreeNode);
  1170. if (pContNode == NULL)
  1171. {
  1172. CADSIEditLeafNode* pLeafNode = dynamic_cast<CADSIEditLeafNode*>(m_pTreeNode);
  1173. ASSERT(pLeafNode != NULL);
  1174. bResult = pLeafNode->BuildSchemaPath(schema);
  1175. }
  1176. else
  1177. {
  1178. bResult = pContNode->BuildSchemaPath(schema);
  1179. }
  1180. if (!bResult)
  1181. {
  1182. return FALSE;
  1183. }
  1184. CADSIQueryObject schemaSearch;
  1185. // Initialize search object with path, username and password
  1186. //
  1187. HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject());
  1188. if (FAILED(hr))
  1189. {
  1190. ADSIEditErrorMessage(hr);
  1191. return FALSE;
  1192. }
  1193. int cCols = 1;
  1194. LPWSTR pszAttributes[] = {L"isSingleValued"};
  1195. LPWSTR pszDesiredAttr = L"attributeSyntax";
  1196. ADS_SEARCH_COLUMN ColumnData;
  1197. hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL);
  1198. if (FAILED(hr))
  1199. {
  1200. ADSIEditErrorMessage(hr);
  1201. return FALSE;
  1202. }
  1203. BOOL bMulti = FALSE;
  1204. CString csFilter;
  1205. csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszProp);
  1206. schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter);
  1207. schemaSearch.SetAttributeList (pszAttributes, cCols);
  1208. hr = schemaSearch.DoQuery ();
  1209. if (SUCCEEDED(hr))
  1210. {
  1211. hr = schemaSearch.GetNextRow();
  1212. if (SUCCEEDED(hr))
  1213. {
  1214. hr = schemaSearch.GetColumn(pszAttributes[0], &ColumnData);
  1215. if (SUCCEEDED(hr))
  1216. {
  1217. TRACE(_T("\t\tisSingleValued: %d\n"),
  1218. ColumnData.pADsValues->Boolean);
  1219. bMulti = !ColumnData.pADsValues->Boolean;
  1220. }
  1221. }
  1222. }
  1223. return bMulti;
  1224. }
  1225. // NOTE : this is only called for the RootDSE or if we failed to get
  1226. // values for the attribute. An empty ADS_ATTR_INFO object is
  1227. // created but should not be modified. If values are to be changed
  1228. // or set for this object a new ADS_ATTR_INFO should be created
  1229. // with the desired block of memory allocated for the values
  1230. //
  1231. CADSIAttr* CAttrEditor::TouchAttr(LPCWSTR lpszAttr)
  1232. {
  1233. // NOTICE-NTRAID#NTBUG9-556322-2002/02/26-artm Need to validate lpszAttr before using.
  1234. // This should never happen, but just in case . . .
  1235. if (!lpszAttr)
  1236. {
  1237. ASSERT(false);
  1238. return NULL;
  1239. }
  1240. POSITION pos = m_ptouchedAttr->FindProperty(lpszAttr);
  1241. if (pos == NULL)
  1242. {
  1243. ADS_ATTR_INFO* pADsInfo = new ADS_ATTR_INFO;
  1244. if (!pADsInfo)
  1245. {
  1246. return 0;
  1247. }
  1248. memset(pADsInfo, 0, sizeof(ADS_ATTR_INFO));
  1249. int iLength = wcslen(lpszAttr);
  1250. pADsInfo->pszAttrName = new WCHAR[iLength + 1];
  1251. wcscpy(pADsInfo->pszAttrName, lpszAttr);
  1252. CADSIQueryObject schemaSearch;
  1253. BOOL bResult;
  1254. CString schema;
  1255. CADSIEditContainerNode* pContNode = m_pConnectData->GetConnectionNode();
  1256. bResult = pContNode->BuildSchemaPath(schema);
  1257. if (!bResult)
  1258. {
  1259. return NULL;
  1260. }
  1261. // Initialize search object with path, username and password
  1262. //
  1263. HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject());
  1264. if (FAILED(hr))
  1265. {
  1266. ADSIEditErrorMessage(hr);
  1267. return NULL;
  1268. }
  1269. int cCols = 3;
  1270. LPWSTR pszAttributes[] = {L"lDAPDisplayName", L"attributeSyntax", L"isSingleValued"};
  1271. LPWSTR pszDesiredAttr = _T("attributeSyntax");
  1272. ADS_SEARCH_COLUMN ColumnData;
  1273. hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL);
  1274. if (FAILED(hr))
  1275. {
  1276. ADSIEditErrorMessage(hr);
  1277. return NULL;
  1278. }
  1279. BOOL bMulti = FALSE;
  1280. CString szSyntax;
  1281. CString csFilter;
  1282. csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszAttr);
  1283. schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter);
  1284. schemaSearch.SetAttributeList (pszAttributes, cCols);
  1285. hr = schemaSearch.DoQuery ();
  1286. if (SUCCEEDED(hr))
  1287. {
  1288. hr = schemaSearch.GetNextRow();
  1289. if (SUCCEEDED(hr))
  1290. {
  1291. hr = schemaSearch.GetColumn(pszDesiredAttr,
  1292. &ColumnData);
  1293. if (SUCCEEDED(hr))
  1294. {
  1295. TRACE(_T("\t\tattributeSyntax: %s\n"),
  1296. ColumnData.pADsValues->CaseIgnoreString);
  1297. ADSTYPE dwType;
  1298. dwType = GetADsTypeFromString(ColumnData.pADsValues->CaseIgnoreString, szSyntax);
  1299. pADsInfo->dwADsType = dwType;
  1300. }
  1301. hr = schemaSearch.GetColumn(pszAttributes[2], &ColumnData);
  1302. if (SUCCEEDED(hr))
  1303. {
  1304. TRACE(_T("\t\tisSingleValued: %d\n"),
  1305. ColumnData.pADsValues->Boolean);
  1306. pADsInfo->dwNumValues = 0;
  1307. bMulti = !ColumnData.pADsValues->Boolean;
  1308. }
  1309. }
  1310. }
  1311. CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, szSyntax, FALSE);
  1312. m_ptouchedAttr->AddTail(pAttr);
  1313. return pAttr;
  1314. }
  1315. return m_ptouchedAttr->GetAt(pos);
  1316. }
  1317. CADSIAttr* CAttrEditor::TouchAttr(ADS_ATTR_INFO* pADsInfo, BOOL bMulti)
  1318. {
  1319. POSITION pos = m_ptouchedAttr->FindProperty(pADsInfo->pszAttrName);
  1320. if (pos == NULL)
  1321. {
  1322. CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, L"");
  1323. m_ptouchedAttr->AddTail(pAttr);
  1324. return pAttr;
  1325. }
  1326. return m_ptouchedAttr->GetAt(pos);
  1327. }