//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: attredit.cpp // //-------------------------------------------------------------------------- #include "pch.h" #include #include "resource.h" #include "common.h" #include "attredit.h" #include "connection.h" #include "attrqry.h" #ifdef DEBUG_ALLOCATOR #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #endif //////////////////////////////////////////////////////////////////////////// // this is used to fill in the attributes for RootDSE // typedef struct tagRootDSEAttr { LPCWSTR lpszAttr; LPCWSTR lpszSyntax; BOOL bMulti; } SYNTAXMAP; extern SYNTAXMAP g_ldapRootDSESyntax[]; extern LPCWSTR g_lpszGC; extern LPCWSTR g_lpszRootDSE; #ifdef OLD_ATTRIBUTE_CLASS /////////////////////////////////////////////////////////////////////////// // CAttrList // If the property is not found in the attribute list, a NULL position is // returned. POSITION CAttrList::FindProperty(LPCWSTR lpszAttr) { CADSIAttr* pAttr; // NOTICE-NTRAID#NTBUG9-556322-2002/05/03-artm Need to validate lpszAttr before using. // This should never happen, but just in case . . . if (!lpszAttr) { ASSERT(false); return NULL; } for (POSITION p = GetHeadPosition(); p != NULL; GetNext(p)) { // I use GetAt here because I don't want to advance the POSITION // because it is returned if they are equal // pAttr = GetAt(p); CString sName; pAttr->GetProperty(sName); // NOTICE-2002/02/25-artm sName should be null terminated // since already in data structure. Validation for lpszAttr // should be done before entering for loop. if (wcscmp(sName, lpszAttr) == 0) { break; } } return p; } BOOL CAttrList::HasProperty(LPCWSTR lpszAttr) { POSITION pos = FindProperty(lpszAttr); return pos != NULL; } // Searches through the cache for the attribute // ppAttr will point to the CADSIAttr if found, NULL if not // void CAttrList::GetNextDirty(POSITION& pos, CADSIAttr** ppAttr) { *ppAttr = GetNext(pos); if (pos == NULL && !(*ppAttr)->IsDirty()) { *ppAttr = NULL; return; } while (!(*ppAttr)->IsDirty() && pos != NULL) { *ppAttr = GetNext(pos); if (!(*ppAttr)->IsDirty() && pos == NULL) { *ppAttr = NULL; break; } } } BOOL CAttrList::HasDirty() { POSITION pos = GetHeadPosition(); while (pos != NULL) { CADSIAttr* pAttr = GetNext(pos); if (pAttr->IsDirty()) { return TRUE; } } return FALSE; } #endif /////////////////////////////////////////////////////////////////////////// // CDNSManageButtonTextHelper CDNSManageButtonTextHelper::CDNSManageButtonTextHelper(int nStates) { m_nID = 0; m_pParentWnd = NULL; m_nStates = nStates; m_lpszText = NULL; m_lpszArr = (LPWSTR*)malloc(sizeof(LPWSTR*)*m_nStates); if (m_lpszArr != NULL) { memset(m_lpszArr, 0x0, sizeof(LPWSTR*)*m_nStates); } // FUTURE-2002/02/25-artm If memory allocation fails, // set the number of states to 0 or maybe even an invalid value. // That way the caller will be able to check to see if things // were allocated correctly. } CDNSManageButtonTextHelper::~CDNSManageButtonTextHelper() { for (int k = 0; k < m_nStates; k++) { if (m_lpszArr[k] != NULL) free(m_lpszArr[k]); } free(m_lpszArr); } void CDNSManageButtonTextHelper::SetStateX(int nIndex) { CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID); ASSERT(pWnd != NULL); ASSERT( (nIndex >0) || (nIndex < m_nStates)); pWnd->SetWindowText(m_lpszArr[nIndex]); } BOOL CDNSManageButtonTextHelper::Init(CWnd* pParentWnd, UINT nButtonID, UINT* nStrArray) { ASSERT(m_pParentWnd == NULL); ASSERT(pParentWnd != NULL); m_pParentWnd = pParentWnd; m_nID = nButtonID; CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID); if (pWnd == NULL) return FALSE; // get the text for the window int nSuccessEntries; // Load the resource strings whose ID's are in nStrArray. LoadStringArrayFromResource(m_lpszArr, nStrArray, m_nStates, &nSuccessEntries); ASSERT(nSuccessEntries == m_nStates); // NTRAID#NTBUG9-554712-2002/02/25-artm No release code backing up assert. // Release code should validate that the resource strings were all correctly // loaded. return TRUE; } /////////////////////////////////////////////////////////////////////////// // CDNSButtonToggleTextHelper CDNSButtonToggleTextHelper::CDNSButtonToggleTextHelper() : CDNSManageButtonTextHelper(2) { } /////////////////////////////////////////////////////////////////////////////// BEGIN_MESSAGE_MAP(CADSIEditBox, CEdit) ON_CONTROL_REFLECT(EN_CHANGE, OnChange) END_MESSAGE_MAP() void CADSIEditBox::OnChange() { m_pEditor->OnEditChange(); } //////////////////////////////////////////////////////////////// // CADSIValueBox BEGIN_MESSAGE_MAP(CADSIValueBox, CEdit) // ON_CONTROL_REFLECT(EN_CHANGE, OnChange) END_MESSAGE_MAP() //////////////////////////////////////////////////////////////// // CADSIValueList BEGIN_MESSAGE_MAP(CADSIValueList, CListBox) ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelChange) END_MESSAGE_MAP() void CADSIValueList::OnSelChange() { m_pEditor->OnValueSelChange(); } //////////////////////////////////////////////////////////////// // CADSIAddButton BEGIN_MESSAGE_MAP(CADSIAddButton, CButton) ON_CONTROL_REFLECT(BN_CLICKED, OnAdd) END_MESSAGE_MAP() void CADSIAddButton::OnAdd() { m_pEditor->OnAddValue(); } //////////////////////////////////////////////////////////////// // CADSIRemoveButton BEGIN_MESSAGE_MAP(CADSIRemoveButton, CButton) ON_CONTROL_REFLECT(BN_CLICKED, OnRemove) END_MESSAGE_MAP() void CADSIRemoveButton::OnRemove() { m_pEditor->OnRemoveValue(); } //////////////////////////////////////////////////////////////// // CAttrEditor CAttrEditor::CAttrEditor() : m_AttrEditBox(this), m_SyntaxBox(this), m_ValueBox(this), m_ValueList(this), m_AddButton(this), m_RemoveButton(this), m_AddButtonHelper(), m_RemoveButtonHelper() { m_bExisting = TRUE; m_ptouchedAttr = NULL; } BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CTreeNode* pTreeNode, LPCWSTR lpszServer, UINT nIDEdit, UINT nIDSyntax, UINT nIDValueBox, UINT nIDValueList, UINT nIDAddButton, UINT nIDRemoveButton, BOOL bComplete) { ASSERT(pParentWnd != NULL); if (pParentWnd == NULL) return FALSE; m_pParentWnd = pParentWnd; m_ptouchedAttr = new CAttrList(); ASSERT(m_ptouchedAttr != NULL); if (pTreeNode == NULL) { m_bExisting = FALSE; } else { m_bExisting = TRUE; } m_sServer = lpszServer; if (m_bExisting) { // This gets the CConnectionData from the ConnectionNode by finding a valid treenode and using its // CADsObject to get the ConnectionNode and then the CConnectionData // m_pTreeNode = pTreeNode; CADSIEditContainerNode* pContNode = dynamic_cast(m_pTreeNode); if (pContNode == NULL) { CADSIEditLeafNode* pLeafNode = dynamic_cast(m_pTreeNode); ASSERT(pLeafNode != NULL); m_pConnectData = pLeafNode->GetADsObject()->GetConnectionNode()->GetConnectionData(); } else { m_pConnectData = pContNode->GetADsObject()->GetConnectionNode()->GetConnectionData(); } } // sublclass controls // BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET }; m_AddButtonHelper.Init(m_pParentWnd, nIDAddButton, nAddButtonTextIDs); bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR }; m_RemoveButtonHelper.Init(m_pParentWnd, nIDRemoveButton, nRemoveButtonTextIDs); // NOTICE-2002/02/27-artm Under low memory environment CString can // throw out of memory exceptions. ADSI edit prefers to shutdown over // trying to handle the exception (since no other components depend on // the tool). if (!m_sNotSet.LoadString(IDS_NOT_SET)) { return FALSE; } if ( bComplete) { // Show property values as single and without the ability to set or clear // SetPropertyUI(0, FALSE, TRUE); } else { m_AttrEditBox.ShowWindow(SW_HIDE); m_SyntaxBox.ShowWindow(SW_HIDE); m_ValueBox.ShowWindow(SW_HIDE); m_ValueList.ShowWindow(SW_HIDE); m_AddButton.ShowWindow(SW_HIDE); m_RemoveButton.ShowWindow(SW_HIDE); } return bRes; } BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CConnectionData* pConnectData, LPCWSTR lpszServer, UINT nIDEdit, UINT nIDSyntax, UINT nIDValueBox, UINT nIDValueList, UINT nIDAddButton, UINT nIDRemoveButton, BOOL bComplete, CAttrList* pAttrList) { ASSERT(pParentWnd != NULL); if (pParentWnd == NULL) return FALSE; m_pParentWnd = pParentWnd; m_bExisting = FALSE; m_sServer = lpszServer; m_pConnectData = pConnectData; ASSERT(pAttrList != NULL); m_ptouchedAttr = pAttrList; // sublclass controls // BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET }; m_AddButtonHelper.Init(m_pParentWnd, nIDAddButton, nAddButtonTextIDs); bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd); ASSERT(bRes); if (!bRes) return FALSE; UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR }; m_RemoveButtonHelper.Init(m_pParentWnd, nIDRemoveButton, nRemoveButtonTextIDs); // NOTICE-2002/02/27-artm Under low memory environment CString can // throw out of memory exceptions. ADSI edit prefers to shutdown over // trying to handle the exception (since no other components depend on // the tool). if (!m_sNotSet.LoadString(IDS_NOT_SET)) { return FALSE; } if ( bComplete) { // Show property values as single and without the ability to set or clear // SetPropertyUI(0, FALSE, TRUE); } else { m_AttrEditBox.ShowWindow(SW_HIDE); m_SyntaxBox.ShowWindow(SW_HIDE); m_ValueBox.ShowWindow(SW_HIDE); m_ValueList.ShowWindow(SW_HIDE); m_AddButton.ShowWindow(SW_HIDE); m_RemoveButton.ShowWindow(SW_HIDE); } return bRes; } void CAttrEditor::SetAttribute(LPCWSTR lpszAttr, LPCWSTR lpszPath) { m_sAttr = lpszAttr; m_sPath = lpszPath; DisplayAttribute(); } BOOL CAttrEditor::OnApply() { if (m_bExisting && m_ptouchedAttr->HasDirty() && !m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC()) { CComPtr pDirObject; // bind to object with authentication // HRESULT hr, hCredResult; hr = OpenObjectWithCredentials( m_pConnectData, m_pConnectData->GetCredentialObject()->UseCredentials(), m_sPath, IID_IDirectoryObject, (LPVOID*) &pDirObject, NULL, hCredResult ); if (FAILED(hr)) { if (SUCCEEDED(hCredResult)) { ADSIEditErrorMessage(hr); m_pParentWnd->SetModified(FALSE); } // Need to change the focus or we will not be able to navigate with the keyboard m_AttrEditBox.SetFocus(); return FALSE; } // Change or add values to ADSI cache that have changed // hr = CADSIAttr::SetValuesInDS(m_ptouchedAttr, pDirObject); if (FAILED(hr)) { //Format Error message and pop up a dialog ADSIEditErrorMessage(hr); m_ptouchedAttr->RemoveAllAttr(); DisplayAttribute(); m_pParentWnd->SetModified(FALSE); // Need to change the focus or we will not be able to navigate with the keyboard m_AttrEditBox.SetFocus(); return FALSE; } } m_pParentWnd->SetModified(FALSE); return TRUE; } void CAttrEditor::OnAddValue() { ASSERT(!m_pConnectData->IsRootDSE()); ASSERT(!m_pConnectData->IsGC()); // NOTICE-2002/02/25-artm Only threat here is that CString will throw out of memory. CString s; m_AttrEditBox.GetWindowText(s); CStringList sList; m_pAttr->GetValues(sList); if (m_pAttr->GetMultivalued()) { // if it is the first value to be added we need to get rid of the "" // CString sNotSet; m_ValueList.GetText(0, sNotSet); if (sNotSet == m_sNotSet) { m_ValueList.ResetContent(); } // then add the new value // sList.AddTail(s); } else { // since it is single valued, remove the old one and add the new one // sList.RemoveAll(); sList.AddTail(s); } HRESULT hr = m_pAttr->SetValues(sList); if (FAILED(hr)) { DisplayFormatError(); } else { if ( m_pAttr->GetMultivalued()) { m_ValueList.AddString(s); } else { m_ValueBox.SetWindowText(s); } m_AttrEditBox.SetWindowText(_T("")); m_pAttr->SetDirty(TRUE); m_pParentWnd->SetModified(TRUE); // Make the UI reflect the new data // m_AttrEditBox.SetFocus(); SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE); // Enable the clear button if the attribute is not multivalued // if ( !m_pAttr->GetMultivalued()) { SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE); } } } void CAttrEditor::DisplayFormatError() { switch (m_pAttr->GetADsType()) { case ADSTYPE_DN_STRING : case ADSTYPE_CASE_EXACT_STRING : case ADSTYPE_CASE_IGNORE_STRING : case ADSTYPE_PRINTABLE_STRING : case ADSTYPE_NUMERIC_STRING : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } case ADSTYPE_BOOLEAN : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_BOOLEAN, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } case ADSTYPE_INTEGER : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } case ADSTYPE_OCTET_STRING : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_OCTET, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } case ADSTYPE_UTC_TIME : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_TIME, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } case ADSTYPE_LARGE_INTEGER : case ADSTYPE_OBJECT_CLASS : case ADSTYPE_UNKNOWN : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } default : { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); if (m_pAttr->GetMultivalued()) { if (m_ValueList.GetCount() < 1) { m_ValueList.AddString(m_sNotSet); } } break; } } } void CAttrEditor::OnRemoveValue() { if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC()) { CStringList sList; m_pAttr->GetValues(sList); DWORD dwNumVals = m_pAttr->GetNumValues(); if (m_pAttr->GetMultivalued()) { CString s, sVal; int iCount = m_ValueList.GetCurSel(); m_ValueList.GetText(iCount, sVal); m_AttrEditBox.SetWindowText(sVal); m_ValueList.DeleteString(iCount); // Add "" to the UI if this is the last value being removed // if (m_ValueList.GetCount() == 0) { m_AttrEditBox.SetFocus(); SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE); m_ValueList.AddString(m_sNotSet); if (!m_bExisting) { m_pAttr->SetDirty(FALSE); } } POSITION pos = sList.FindIndex(iCount); sList.RemoveAt(pos); HRESULT hr = m_pAttr->SetValues(sList); if (FAILED(hr)) { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); } if (m_bExisting || m_ValueList.GetCount() > 0) { m_pAttr->SetDirty(TRUE); } } else { // NOTICE-2002/02/25-artm Only threat is that CString will throw // out of memory exception. The input is as safe as can be, coming // from UI and there is no manipulation. CString sVal; m_ValueBox.GetWindowText(sVal); m_AttrEditBox.SetWindowText(sVal); m_ValueBox.SetWindowText( m_sNotSet); sList.RemoveAll(); HRESULT hr = m_pAttr->SetValues(sList); if (FAILED(hr)) { ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK); } if (!m_bExisting) { m_pAttr->SetDirty(FALSE); } else { m_pAttr->SetDirty(TRUE); } } m_AttrEditBox.SetFocus(); SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE); dwNumVals--; m_pParentWnd->SetModified(TRUE); } } void CAttrEditor::OnEditChange() { if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC()) { CString s; // NOTICE-2002/02/25-artm Only threat is that CString can throw // out of memory exceptions. m_AttrEditBox.GetWindowText(s); if (s != _T("")) { SetPropertyUI(TN_FLAG_ENABLE_ADD, FALSE); } else { SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE); } } } void CAttrEditor::OnValueSelChange() { if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC()) { SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE); } } void CAttrEditor::GetAttrFailed() { CString sSyntax; GetSyntax(m_sAttr, sSyntax); m_SyntaxBox.SetWindowText(sSyntax); m_ValueList.ResetContent(); m_ValueList.AddString(m_sNotSet); m_ValueBox.SetWindowText(m_sNotSet); SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE); } void CAttrEditor::FillWithExisting() { CString s; m_pAttr = m_ptouchedAttr->GetAt(m_ptouchedAttr->FindProperty(m_sAttr)); ASSERT(m_pAttr != NULL); CStringList slValues; m_pAttr->GetValues(slValues); if (m_pAttr->GetMultivalued()) { m_ValueList.ResetContent(); POSITION pos; if (slValues.GetCount() == 0) { slValues.AddTail(m_sNotSet); } for (pos = slValues.GetHeadPosition(); pos != NULL; slValues.GetNext(pos) ) { s = slValues.GetAt(pos); m_ValueList.AddString(s); } SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE); } else { if (slValues.GetCount() > 0) { s = slValues.GetAt(slValues.GetHeadPosition()); m_ValueBox.SetWindowText(s); if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC()) { SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE); } } else { m_ValueBox.SetWindowText(m_sNotSet); SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE); } SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); } } void CAttrEditor::DisplayAttribute() { int iCount; HRESULT hr, hCredResult; if (m_ptouchedAttr->HasProperty(m_sAttr)) { FillWithExisting(); } else { if (m_pConnectData->IsRootDSE()) { DisplayRootDSE(); } else if (!m_bExisting) { ADS_ATTR_INFO *pAttrInfo = NULL; GetAttrFailed(); m_pAttr = TouchAttr(m_sAttr); ASSERT(m_pAttr != NULL); if (m_pAttr != NULL) { if (m_pAttr->GetMultivalued()) { SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE); } else { SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); } } return; } else { CComPtr pDirObject; hr = OpenObjectWithCredentials( m_pConnectData, m_pConnectData->GetCredentialObject()->UseCredentials(), m_sPath, IID_IDirectoryObject, (LPVOID*) &pDirObject, NULL, hCredResult ); if ( FAILED(hr) ) { if (SUCCEEDED(hCredResult)) { ADSIEditErrorMessage(hr); } return; } ASSERT(pDirObject != NULL); // Get attribute // CString szAttrName; szAttrName = m_sAttr + _T(";range=0-*"); CString szFormat = m_sAttr + _T(";range=%ld-*"); DWORD dwReturn = 0; DWORD dwNumAttr = 1; ADS_ATTR_INFO *pAttrInfo; const WCHAR wcSep = L'-'; const WCHAR wcEnd = L'*'; BOOL fMoreRemain = FALSE; CStringList sList; do { LPWSTR lpszAttrs[] = {(LPWSTR)(LPCWSTR)szAttrName}; hr = pDirObject->GetObjectAttributes(lpszAttrs, dwNumAttr, &pAttrInfo, &dwReturn); if (FAILED(hr)) { ADSIEditErrorMessage(hr); return; } if (pAttrInfo == NULL) { GetAttrFailed(); m_pAttr = TouchAttr(m_sAttr); ASSERT(m_pAttr != NULL); if (m_pAttr != NULL) { if (m_pAttr->GetMultivalued()) { SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE); } else { SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); } } return; } if (dwReturn > 0) { GetStringFromADs(pAttrInfo, sList); } // // Check to see if there is more data. If the last char of the // attribute name string is an asterisk, then we have everything. // // NOTICE-2002/02/25-artm This is trusting that AD is null // terminating the name of the attribute. The reasons why this // trust in service outside the component is allowable are: // 1) If AD is being spoofed the attacker will 'only' get us to read // bogus memory. This will AV the client app. // 2) There is no way to validate the length of the attribute name // string with the current ADS_ATTR_INFO structure. int cchEnd = wcslen(pAttrInfo->pszAttrName); fMoreRemain = pAttrInfo->pszAttrName[cchEnd - 1] != wcEnd; if (fMoreRemain) { PWSTR pwz = wcsrchr(pAttrInfo->pszAttrName, wcSep); if (!pwz) { ASSERT(FALSE && pAttrInfo->pszAttrName); fMoreRemain = FALSE; } else { pwz++; // move past the hyphen to the range end value. // NOTICE-2002/02/27-artm Assert does not require release code. // The assert is a sanity check that the ADSI interface did not return an // undocumented string format. In other words, there should never be a time // when this branch is entered and the '-' is the last character in the string. // The release code does not need this check b/c if it does occur (and it shouldn't), // there's a bug in ADSI. The tool will crash from reading past pointer and go // into Dr. Watson (which can be used just as well to track down the bug). ASSERT(*pwz); long lEnd = _wtol(pwz); lEnd++; // start with the next value. szAttrName.Format(szFormat, lEnd); TRACE(L"Range returned is %ws, now asking for %ws\n", pAttrInfo->pszAttrName, szAttrName); } } } while (fMoreRemain); BOOL bMulti = FALSE; if (m_pConnectData->IsGC()) { bMulti = IsMultiValued(pAttrInfo); } else { bMulti = IsMultiValued(m_sAttr); } if (pAttrInfo != NULL) { if (bMulti) { SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE); m_ValueList.ResetContent(); POSITION pos = sList.GetHeadPosition(); while (pos != NULL) { CString sValue = sList.GetNext(pos); m_ValueList.AddString(sValue); } } else { SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); if (sList.GetCount() > 0) { m_ValueBox.SetWindowText(sList.GetHead()); } if (!m_pConnectData->IsGC()) { SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE); } } } else { GetAttrFailed(); CStringList sTempList; m_pAttr = TouchAttr(pAttrInfo, bMulti); if (bMulti) { SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE); } else { SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); } return; } m_pAttr = TouchAttr(pAttrInfo, bMulti); } } CString sSyntax; GetSyntax(m_sAttr, sSyntax); m_SyntaxBox.SetWindowText(sSyntax); } void CAttrEditor::DisplayRootDSE() { CString s = m_sPath; CComPtr pADs; HRESULT hr, hCredResult; hr = OpenObjectWithCredentials( m_pConnectData, m_pConnectData->GetCredentialObject()->UseCredentials(), s, IID_IADs, (LPVOID*)&pADs, NULL, hCredResult ); if ( FAILED(hr) ) { if (SUCCEEDED(hCredResult)) { ADSIEditErrorMessage(hr); } return; } // This is to insure that the ADSI cache is current // hr = pADs->GetInfo(); VARIANT var; hr = pADs->GetEx( CComBSTR(m_sAttr) , &var ); if ( FAILED(hr) ) { GetAttrFailed(); m_pAttr = TouchAttr(m_sAttr); return; } ///////////////////////////////////////// // Convert and populate /////////////////////////////////////////// CStringList sList; hr = VariantToStringList( var, sList ); if ( FAILED(hr) ) { GetAttrFailed(); VariantClear(&var); CStringList sTempList; m_pAttr = TouchAttr(m_sAttr); return; } VariantClear( &var ); if ( IsRootDSEAttrMultiValued(m_sAttr) ) { SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE); m_ValueList.ResetContent(); POSITION pos = sList.GetHeadPosition(); while (pos != NULL) { CString sValue = sList.GetNext(pos); m_ValueList.AddString(sValue); } } else { SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE); s = sList.GetHead(); m_ValueBox.SetWindowText(s); } // m_pAttr = TouchAttr(m_sAttr); CString sSyntax; GetSyntax(m_sAttr, sSyntax); m_SyntaxBox.SetWindowText(sSyntax); // REVEIW : this is the only occurrance of "UTCTime", if there // becomes more we may need to make a global string or something // if (sSyntax == _T("UTCTime")) { CString sFormatted, sRemainder; CString sYear, sMonth, sDay, sHour, sMinute, sSecond; int iCount = 0; sYear = s.Left(4); iCount = s.GetLength(); sRemainder = s.Right(iCount - 4); sMonth = sRemainder.Left(2); iCount = sRemainder.GetLength(); sRemainder = sRemainder.Right(iCount - 2); sDay = sRemainder.Left(2); iCount = sRemainder.GetLength(); sRemainder = sRemainder.Right(iCount - 2); sHour = sRemainder.Left(2); iCount = sRemainder.GetLength(); sRemainder = sRemainder.Right(iCount - 2); sMinute = sRemainder.Left(2); iCount = sRemainder.GetLength(); sRemainder = sRemainder.Right(iCount - 2); sSecond = sRemainder.Left(2); sFormatted = sMonth + _T("/") + sDay + _T("/") + sYear + _T(" ") + sHour + _T(":") + sMinute + _T(":") + sSecond; m_ValueBox.SetWindowText(sFormatted); } } // Pre: lpszAttr non-NULL and must be null terminated BOOL CAttrEditor::IsRootDSEAttrMultiValued(LPCWSTR lpszAttr) { int idx=0, iCount = 0; // NOTICE-2002/02/26-artm This method only called from SetAttribute() // and is not a public function. If it were public we would need to be // careful about a NULL lpszAttr and potentially a string that is not // null terminated. iCount = wcslen(lpszAttr); while( g_ldapRootDSESyntax[idx].lpszAttr) { // NOTICE-2002/02/26-artm Comparison well bounded by length // of lpszAttr. if ( _wcsnicmp(g_ldapRootDSESyntax[idx].lpszAttr, lpszAttr, iCount) == 0) { return g_ldapRootDSESyntax[idx].bMulti; } idx++; } return FALSE; } // TODO : This is extremely ugly, redo it // void CAttrEditor::SetPropertyUI(DWORD dwFlags, BOOL bAnd, BOOL bReset) { if (bReset) { m_dwMultiFlags = dwFlags; } if (bAnd) { m_dwMultiFlags &= dwFlags; } else { m_dwMultiFlags |= dwFlags; } if (m_dwMultiFlags & TN_FLAG_SHOW_MULTI) { m_AddButtonHelper.SetToggleState(TRUE); m_RemoveButtonHelper.SetToggleState(TRUE); m_ValueList.ShowWindow(SW_SHOW); m_ValueBox.ShowWindow(SW_HIDE); } else { m_AddButtonHelper.SetToggleState(FALSE); m_RemoveButtonHelper.SetToggleState(FALSE); m_ValueList.ShowWindow(SW_HIDE); m_ValueBox.ShowWindow(SW_SHOW); } if (m_dwMultiFlags & TN_FLAG_ENABLE_REMOVE) { m_RemoveButton.EnableWindow(TRUE); } else { m_RemoveButton.EnableWindow(FALSE); } if (m_dwMultiFlags & TN_FLAG_ENABLE_ADD) { m_AddButton.EnableWindow(TRUE); } else { m_AddButton.EnableWindow(FALSE); } if (m_bExisting && (m_pConnectData->IsGC() || m_pConnectData->IsRootDSE())) { m_AttrEditBox.EnableWindow(FALSE); } else { m_AttrEditBox.EnableWindow(TRUE); } } // Pre: lpszProp non-NULL and null terminated string void CAttrEditor::GetSyntax(LPCWSTR lpszProp, CString& sSyntax) { if (m_bExisting && m_pConnectData->IsRootDSE()) { int idx=0; while( g_ldapRootDSESyntax[idx].lpszAttr ) { // NOTICE-2002/02/26-artm Use of wcscmp() is ok b/c this is // a protected function. If it were public we would need to // worry about a NULL pointer and strings that weren't null // terminated. if ( wcscmp(lpszProp, g_ldapRootDSESyntax[idx].lpszAttr) == 0 ) { sSyntax = g_ldapRootDSESyntax[idx].lpszSyntax; return; } idx++; } } else { CComPtr pProp; HRESULT hr, hCredResult; CString schema; m_pConnectData->GetAbstractSchemaPath(schema); schema = schema + lpszProp; hr = OpenObjectWithCredentials( m_pConnectData, m_pConnectData->GetCredentialObject()->UseCredentials(), schema, IID_IADsProperty, (LPVOID*) &pProp, NULL, hCredResult ); if ( FAILED(hr) ) { if (SUCCEEDED(hCredResult)) { ADSIEditErrorMessage(hr); } return; } /////////////////////////////////////////////////// // Create a new cached attribute and populate ////////////////////////////////////////////////// BSTR bstr; hr = pProp->get_Syntax( &bstr ); if ( SUCCEEDED(hr) ) { sSyntax = bstr; } SysFreeString(bstr); } } BOOL CAttrEditor::IsMultiValued(ADS_ATTR_INFO* pAttrInfo) { return (pAttrInfo->dwNumValues > 1) ? TRUE : FALSE; } BOOL CAttrEditor::IsMultiValued(LPCWSTR lpszProp) { CString schema; BOOL bResult = FALSE; CADSIEditContainerNode* pContNode = dynamic_cast(m_pTreeNode); if (pContNode == NULL) { CADSIEditLeafNode* pLeafNode = dynamic_cast(m_pTreeNode); ASSERT(pLeafNode != NULL); bResult = pLeafNode->BuildSchemaPath(schema); } else { bResult = pContNode->BuildSchemaPath(schema); } if (!bResult) { return FALSE; } CADSIQueryObject schemaSearch; // Initialize search object with path, username and password // HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject()); if (FAILED(hr)) { ADSIEditErrorMessage(hr); return FALSE; } int cCols = 1; LPWSTR pszAttributes[] = {L"isSingleValued"}; LPWSTR pszDesiredAttr = L"attributeSyntax"; ADS_SEARCH_COLUMN ColumnData; hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL); if (FAILED(hr)) { ADSIEditErrorMessage(hr); return FALSE; } BOOL bMulti = FALSE; CString csFilter; csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszProp); schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter); schemaSearch.SetAttributeList (pszAttributes, cCols); hr = schemaSearch.DoQuery (); if (SUCCEEDED(hr)) { hr = schemaSearch.GetNextRow(); if (SUCCEEDED(hr)) { hr = schemaSearch.GetColumn(pszAttributes[0], &ColumnData); if (SUCCEEDED(hr)) { TRACE(_T("\t\tisSingleValued: %d\n"), ColumnData.pADsValues->Boolean); bMulti = !ColumnData.pADsValues->Boolean; } } } return bMulti; } // NOTE : this is only called for the RootDSE or if we failed to get // values for the attribute. An empty ADS_ATTR_INFO object is // created but should not be modified. If values are to be changed // or set for this object a new ADS_ATTR_INFO should be created // with the desired block of memory allocated for the values // CADSIAttr* CAttrEditor::TouchAttr(LPCWSTR lpszAttr) { // NOTICE-NTRAID#NTBUG9-556322-2002/02/26-artm Need to validate lpszAttr before using. // This should never happen, but just in case . . . if (!lpszAttr) { ASSERT(false); return NULL; } POSITION pos = m_ptouchedAttr->FindProperty(lpszAttr); if (pos == NULL) { ADS_ATTR_INFO* pADsInfo = new ADS_ATTR_INFO; if (!pADsInfo) { return 0; } memset(pADsInfo, 0, sizeof(ADS_ATTR_INFO)); int iLength = wcslen(lpszAttr); pADsInfo->pszAttrName = new WCHAR[iLength + 1]; wcscpy(pADsInfo->pszAttrName, lpszAttr); CADSIQueryObject schemaSearch; BOOL bResult; CString schema; CADSIEditContainerNode* pContNode = m_pConnectData->GetConnectionNode(); bResult = pContNode->BuildSchemaPath(schema); if (!bResult) { return NULL; } // Initialize search object with path, username and password // HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject()); if (FAILED(hr)) { ADSIEditErrorMessage(hr); return NULL; } int cCols = 3; LPWSTR pszAttributes[] = {L"lDAPDisplayName", L"attributeSyntax", L"isSingleValued"}; LPWSTR pszDesiredAttr = _T("attributeSyntax"); ADS_SEARCH_COLUMN ColumnData; hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL); if (FAILED(hr)) { ADSIEditErrorMessage(hr); return NULL; } BOOL bMulti = FALSE; CString szSyntax; CString csFilter; csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszAttr); schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter); schemaSearch.SetAttributeList (pszAttributes, cCols); hr = schemaSearch.DoQuery (); if (SUCCEEDED(hr)) { hr = schemaSearch.GetNextRow(); if (SUCCEEDED(hr)) { hr = schemaSearch.GetColumn(pszDesiredAttr, &ColumnData); if (SUCCEEDED(hr)) { TRACE(_T("\t\tattributeSyntax: %s\n"), ColumnData.pADsValues->CaseIgnoreString); ADSTYPE dwType; dwType = GetADsTypeFromString(ColumnData.pADsValues->CaseIgnoreString, szSyntax); pADsInfo->dwADsType = dwType; } hr = schemaSearch.GetColumn(pszAttributes[2], &ColumnData); if (SUCCEEDED(hr)) { TRACE(_T("\t\tisSingleValued: %d\n"), ColumnData.pADsValues->Boolean); pADsInfo->dwNumValues = 0; bMulti = !ColumnData.pADsValues->Boolean; } } } CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, szSyntax, FALSE); m_ptouchedAttr->AddTail(pAttr); return pAttr; } return m_ptouchedAttr->GetAt(pos); } CADSIAttr* CAttrEditor::TouchAttr(ADS_ATTR_INFO* pADsInfo, BOOL bMulti) { POSITION pos = m_ptouchedAttr->FindProperty(pADsInfo->pszAttrName); if (pos == NULL) { CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, L""); m_ptouchedAttr->AddTail(pAttr); return pAttr; } return m_ptouchedAttr->GetAt(pos); }