//+---------------------------------------------------------------------------- // // Windows NT Directory Service Property Pages // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2001 // // File: user.cxx // // Contents: AD user shell property pages // // History: 05-May-97 EricB created // //----------------------------------------------------------------------------- #include "pch.h" #include "proppage.h" #include "objlist.h" #include "user.h" //#include "group.h" #ifndef UNICODE # include #endif //+---------------------------------------------------------------------------- // // Function: CountryCode // // Synopsis: Handles the Country combo box to get/set the Country-Code // (LDAP display name: countryCode) numeric ISO-3166 code. // // Notes: This attr function MUST be called after CountryName. It // relies on CountryName populating the combobox and setting its // item data values. // //----------------------------------------------------------------------------- HRESULT CountryCode(CDsPropPageBase * pPage, PATTR_MAP pAttrMap, PADS_ATTR_INFO pAttrInfo, LPARAM lParam, PATTR_DATA pAttrData, DLG_OP DlgOp) { switch(DlgOp) { case fOnCommand: if (CBN_SELCHANGE == lParam) { pPage->SetDirty(); PATTR_DATA_SET_DIRTY(pAttrData); } break; case fApply: DBG_OUT("CountryCode: fApply"); if (!PATTR_DATA_IS_WRITABLE(pAttrData) || !PATTR_DATA_IS_DIRTY(pAttrData)) { return ADM_S_SKIP; } int iSel = (int)SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETCURSEL, 0, 0); if (iSel < 0) { pAttrInfo->dwControlCode = ADS_ATTR_CLEAR; } else { INT_PTR pCur = SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETITEMDATA, iSel, 0); if (pCur == LB_ERR) { DWORD dwErr = GetLastError(); CHECK_WIN32_REPORT(dwErr, pPage->GetHWnd(), return HRESULT_FROM_WIN32(dwErr);); } PDsCountryCode pCountryCode = (PDsCountryCode)pCur; PADSVALUE pADsValue; pADsValue = new ADSVALUE; CHECK_NULL_REPORT(pADsValue, pPage->GetHWnd(), return E_OUTOFMEMORY); pAttrInfo->pADsValues = pADsValue; pAttrInfo->dwNumValues = 1; pAttrInfo->dwControlCode = ADS_ATTR_UPDATE; pADsValue->dwType = pAttrInfo->dwADsType; pADsValue->Integer = pCountryCode->wCode; } break; } return S_OK; } //+---------------------------------------------------------------------------- // // Function: CountryName // // Synopsis: Handles the Country combo box/static control to get/set the // Country-Name (LDAP display name: c) 2 character ISO-3166 code. // // Notes: If the control is read-only, then assume it is a static text // control (or a read-only edit control) rather than a combobox. // Also, if read-only, then only the fInit should be called. // //----------------------------------------------------------------------------- HRESULT CountryName(CDsPropPageBase * pPage, PATTR_MAP pAttrMap, PADS_ATTR_INFO pAttrInfo, LPARAM lParam, PATTR_DATA pAttrData, DLG_OP DlgOp) { PWSTR pwsz = NULL; DWORD dwErr = 0; INT_PTR pCur = NULL; PDsCountryCode pCountryCode = NULL; int iSel = 0, iCur = -1, cxExtent = 0; #ifdef UNICODE CStrW strFirstCode, strLastCode, strCodeLine, strCurName; #else CStr strFirstCode, strLastCode, strCodeLine, strCurName; #endif CStrW str2CharAbrev; switch (DlgOp) { case fInit: dspAssert(pAttrData); if (!pAttrMap->fIsReadOnly && !PATTR_DATA_IS_WRITABLE(pAttrData)) { EnableWindow(GetDlgItem(pPage->GetHWnd(), pAttrMap->nCtrlID), FALSE); // // Remove the accelerator from the label since a disabled control // is not reachable. // CStr strLabel; strLabel.LoadString(g_hInstance, IDS_COUNTRY_LABEL_NO_ACCEL); SetDlgItemText(pPage->GetHWnd(), IDC_COUNTRY_LABEL, strLabel.GetBuffer(0)); } // fall through... case fObjChanged: { DBG_OUT("CountryName: fInit"); PTSTR ptzFullName = NULL; WORD wCode = 0; HDC hDC = NULL; unsigned long ulFirstCode, ulLastCode, i; strFirstCode.LoadString(g_hInstance, IDS_FIRST_COUNTRY_CODE); strLastCode.LoadString(g_hInstance, IDS_LAST_COUNTRY_CODE); if (strFirstCode.IsEmpty() || strLastCode.IsEmpty()) { ERR_MSG(IDS_ERR_COUNTRY_DATA_BAD, pPage->GetHWnd()); return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } ulFirstCode = _tcstoul(strFirstCode, NULL, 10); ulLastCode = _tcstoul(strLastCode, NULL, 10); if (!pAttrMap->fIsReadOnly) { hDC = GetDC(GetDlgItem(pPage->GetHWnd(), pAttrMap->nCtrlID)); } PWSTR pwzCurCode; if (pAttrInfo && pAttrInfo->dwNumValues) { pwzCurCode = pAttrInfo->pADsValues->CaseIgnoreString; } else { pwzCurCode = NULL; } for (i = ulFirstCode; i <= ulLastCode; i++) { strCodeLine.LoadString(g_hInstance, i); if (strCodeLine.IsEmpty()) { ERR_MSG(IDS_ERR_COUNTRY_DATA_BAD, pPage->GetHWnd()); return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } if (!GetALineOfCodes(strCodeLine.GetBuffer(1), &ptzFullName, str2CharAbrev, &wCode)) { ERR_MSG(IDS_ERR_COUNTRY_DATA_BAD, pPage->GetHWnd()); return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } if (!pAttrMap->fIsReadOnly) { // If not in read-only mode, then we use a combobox from which // the user selects the country. // Insert the full name into the combobox list. // SIZE s; if (hDC != NULL) { GetTextExtentPoint32(hDC, ptzFullName, static_cast(_tcslen(ptzFullName)), &s); if (s.cx > cxExtent) { cxExtent = s.cx; } iSel = (int)SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_ADDSTRING, 0, (LPARAM)ptzFullName); if (iSel < 0) { CHECK_HRESULT_REPORT(E_OUTOFMEMORY, pPage->GetHWnd(), return E_OUTOFMEMORY); } // // Add the name codes as item data. // pCountryCode = new DsCountryCode; CHECK_NULL_REPORT(pCountryCode, pPage->GetHWnd(), return E_OUTOFMEMORY); wcscpy(pCountryCode->pwz2CharAbrev, str2CharAbrev); pCountryCode->wCode = wCode; if (SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_SETITEMDATA, iSel, (LPARAM)pCountryCode) == CB_ERR) { CHECK_HRESULT_REPORT(E_OUTOFMEMORY, pPage->GetHWnd(), return E_OUTOFMEMORY); } } } // // See if the current country matches that saved on the DS object // (if one has been saved). // if (pwzCurCode) { if (_wcsicmp(pwzCurCode, str2CharAbrev) == 0) { iCur = iSel; strCurName = ptzFullName; } } if ((iCur == iSel) && pAttrMap->fIsReadOnly) { // Read-only mode means that we are using a static text // control. Insert the full name into the control. // SetDlgItemText(pPage->GetHWnd(), pAttrMap->nCtrlID, ptzFullName); break; } if (pAttrMap->fIsReadOnly) { iSel++; } } if (!pAttrMap->fIsReadOnly) { if (hDC != NULL) { ReleaseDC(GetDlgItem(pPage->GetHWnd(), pAttrMap->nCtrlID), hDC); } SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_SETHORIZONTALEXTENT, (WPARAM)cxExtent, 0); if (iCur >= 0) { iCur = (int) SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_FINDSTRINGEXACT, 0, (WPARAM)(LPCTSTR)strCurName); dspAssert(iCur != CB_ERR); SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_SETCURSEL, iCur, 0); } } else { if (iCur < 0) { // If iCur is still -1, then country code hasn't been set. // SetDlgItemText(pPage->GetHWnd(), pAttrMap->nCtrlID, TEXT("")); } } break; } case fApply: DBG_OUT("CountryName: fApply"); if (!PATTR_DATA_IS_WRITABLE(pAttrData) || !PATTR_DATA_IS_DIRTY(pAttrData)) { return ADM_S_SKIP; } iSel = (int)SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETCURSEL, 0, 0); if (iSel < 0) { pAttrInfo->dwControlCode = ADS_ATTR_CLEAR; } else { pCur = SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETITEMDATA, iSel, 0); if (pCur == LB_ERR) { dwErr = GetLastError(); CHECK_WIN32_REPORT(dwErr, pPage->GetHWnd(), return HRESULT_FROM_WIN32(dwErr);); } pCountryCode = (PDsCountryCode)pCur; if (!AllocWStr(pCountryCode->pwz2CharAbrev, &pwsz)) { CHECK_HRESULT_REPORT(E_OUTOFMEMORY, pPage->GetHWnd(), return E_OUTOFMEMORY); } PADSVALUE pADsValue; pADsValue = new ADSVALUE; CHECK_NULL_REPORT(pADsValue, pPage->GetHWnd(), return E_OUTOFMEMORY); pAttrInfo->pADsValues = pADsValue; pAttrInfo->dwNumValues = 1; pAttrInfo->dwControlCode = ADS_ATTR_UPDATE; pADsValue->dwType = pAttrInfo->dwADsType; pADsValue->CaseIgnoreString = pwsz; } break; case fOnCommand: if (CBN_SELCHANGE == lParam) { pPage->SetDirty(); PATTR_DATA_SET_DIRTY(pAttrData); } break; case fOnDestroy: DBG_OUT("CountryName: fOnDestroy"); iSel = 0; do { pCur = SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETITEMDATA, iSel, 0); if (pCur != CB_ERR) { // Delete the itemdata string. // delete (PDsCountryCode)pCur; iSel++; } } while (pCur != CB_ERR); break; } return S_OK; } //+---------------------------------------------------------------------------- // // Function: TextCountry // // Synopsis: Handles the Country combo box to get/set the Text-Country // (LDAP display name: co) // // Notes: This attr function MUST be called after CountryName. It // relies on CountryName populating the combobox // //----------------------------------------------------------------------------- HRESULT TextCountry(CDsPropPageBase * pPage, PATTR_MAP pAttrMap, PADS_ATTR_INFO pAttrInfo, LPARAM lParam, PATTR_DATA pAttrData, DLG_OP DlgOp) { switch(DlgOp) { case fOnCommand: if (CBN_SELCHANGE == lParam) { pPage->SetDirty(); PATTR_DATA_SET_DIRTY(pAttrData); } break; case fApply: DBG_OUT("TextCountry: fApply"); if (!PATTR_DATA_IS_WRITABLE(pAttrData) || !PATTR_DATA_IS_DIRTY(pAttrData)) { return ADM_S_SKIP; } int iSel = (int)SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETCURSEL, 0, 0); if (iSel < 0) { pAttrInfo->dwControlCode = ADS_ATTR_CLEAR; } else { LPTSTR ptz = new TCHAR[pAttrMap->nSizeLimit + 1]; CHECK_NULL_REPORT(ptz, pPage->GetHWnd(), return E_OUTOFMEMORY); INT_PTR pCur = SendDlgItemMessage(pPage->GetHWnd(), pAttrMap->nCtrlID, CB_GETLBTEXT, iSel, (LPARAM) (LPCSTR) ptz ); if (pCur == LB_ERR) { delete ptz; DWORD dwErr = GetLastError(); CHECK_WIN32_REPORT(dwErr, pPage->GetHWnd(), return HRESULT_FROM_WIN32(dwErr);); } PADSVALUE pADsValue; pADsValue = new ADSVALUE; if( pADsValue == NULL ) { ReportError(E_OUTOFMEMORY,0, pPage->GetHWnd()); delete ptz; return E_OUTOFMEMORY; } if (!TcharToUnicode(ptz, &pADsValue->CaseIgnoreString)) { delete ptz; delete pADsValue; REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); return E_OUTOFMEMORY; } delete ptz; pAttrInfo->pADsValues = pADsValue; pAttrInfo->dwNumValues = 1; pAttrInfo->dwControlCode = ADS_ATTR_UPDATE; pADsValue->dwType = pAttrInfo->dwADsType; } break; } return S_OK; } // CountryName helpers: //+---------------------------------------------------------------------------- // // Function: GetALineOfCodes // // Synopsis: Parse a line of country codes. // //----------------------------------------------------------------------------- BOOL GetALineOfCodes(PTSTR ptzLine, PTSTR * pptzFullName, CStrW & str2CharAbrev, LPWORD pwCode) { // // The line is parsed from end to beginning. That way we don't need to // dependend on the column widths being fixed. // // The last token is the numeric code. Read it. // RemoveTrailingWhitespace(ptzLine); PTSTR ptzCode = _tcsrchr(ptzLine, TEXT(' ')); if (!ptzCode) { // try tab char. // ptzCode = _tcsrchr(ptzLine, TEXT('\t')); } if (!ptzCode || (ptzCode <= ptzLine) || (_tcslen(ptzCode) < 2)) { return FALSE; } *ptzCode = TEXT('\0'); ptzCode++; int iScanned = _stscanf(ptzCode, TEXT("%u"), pwCode); dspAssert(iScanned == 1); // // The next to last token is the 3 character code. Skip it. // RemoveTrailingWhitespace(ptzLine); size_t nLen = _tcslen(ptzLine); if (3 >= nLen) { return FALSE; } ptzLine[nLen - 3] = TEXT('\0'); // // The next token (moving toward the front) is the 2 character code. // RemoveTrailingWhitespace(ptzLine); PTSTR ptz2CharAbrev = _tcsrchr(ptzLine, TEXT(' ')); if (!ptz2CharAbrev) { // try tab char. // ptz2CharAbrev = _tcsrchr(ptzLine, TEXT('\t')); } if (!ptz2CharAbrev || (ptz2CharAbrev <= ptzLine)) { return FALSE; } *ptz2CharAbrev = TEXT('\0'); ptz2CharAbrev++; if (_tcslen(ptz2CharAbrev) != 2) { return FALSE; } str2CharAbrev = ptz2CharAbrev; // // The first token is the full country name. // RemoveTrailingWhitespace(ptzLine); if (!_tcslen(ptzLine)) { return FALSE; } *pptzFullName = ptzLine; return TRUE; } //+---------------------------------------------------------------------------- // // Function: RemoveTrailingWhitespace // // Synopsis: Trailing white space is replaced by NULLs. // //----------------------------------------------------------------------------- void RemoveTrailingWhitespace(PTSTR ptz) { size_t nLen = _tcslen(ptz); while (nLen) { if (!iswspace(ptz[nLen - 1])) { return; } ptz[nLen - 1] = L'\0'; nLen--; } } //+---------------------------------------------------------------------------- // // Function: ManagerEdit // // Synopsis: Handles the manager edit control. // // Notes: The page member m_pData stores the pAttrData value whose pVoid // element is set to the DN of the manager. The other manager // attr functions can then access the manager value and can also // read the fWritable element. // //----------------------------------------------------------------------------- HRESULT ManagerEdit(CDsPropPageBase * pPage, PATTR_MAP pAttrMap, PADS_ATTR_INFO pAttrInfo, LPARAM lParam, PATTR_DATA pAttrData, DLG_OP DlgOp) { PWSTR pwz = NULL; PWSTR canonical = NULL; HRESULT hr = S_OK; switch (DlgOp) { case fInit: dspAssert(pAttrData); if (pAttrInfo && pAttrInfo->dwNumValues > 0) { if (!AllocWStr(pAttrInfo->pADsValues[0].DNString, &pwz)) { REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); return E_OUTOFMEMORY; } hr = CrackName (pwz, &canonical, GET_OBJ_CAN_NAME_EX, pPage->GetHWnd()); if (FAILED(hr)) { delete pwz; return S_FALSE; } PTSTR ptz, ptzName; if (!UnicodeToTchar(canonical, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); delete pwz; return E_OUTOFMEMORY; } LocalFreeStringW(&canonical); ptzName = _tcschr(ptz, TEXT('\n')); dspAssert(ptzName); ptzName++; SetDlgItemText(pPage->GetHWnd(), pAttrMap->nCtrlID, ptzName); delete ptz; } pAttrData->pVoid = reinterpret_cast(pwz); ((CDsTableDrivenPage *)pPage)->m_pData = reinterpret_cast(pAttrData); break; case fOnCommand: if (EN_CHANGE == lParam) { pPage->SetDirty(); PATTR_DATA_SET_DIRTY(pAttrData); } break; case fApply: if (!PATTR_DATA_IS_WRITABLE(pAttrData) || !PATTR_DATA_IS_DIRTY(pAttrData)) { return ADM_S_SKIP; } pwz = (PWSTR)reinterpret_cast(((CDsTableDrivenPage *)pPage)->m_pData)->pVoid; if (pwz) { // make a copy cause CDsTableDrivenPage::OnApply deletes it. PWSTR pwzTmp; if (!AllocWStr(pwz, &pwzTmp)) { REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); return E_OUTOFMEMORY; } PADSVALUE pADsValue; pADsValue = new ADSVALUE; CHECK_NULL(pADsValue, return E_OUTOFMEMORY); pAttrInfo->pADsValues = pADsValue; pAttrInfo->dwNumValues = 1; pAttrInfo->dwControlCode = ADS_ATTR_UPDATE; pADsValue->dwType = pAttrInfo->dwADsType; pADsValue->CaseIgnoreString = pwzTmp; } else { pAttrInfo->pADsValues = NULL; pAttrInfo->dwNumValues = 0; pAttrInfo->dwControlCode = ADS_ATTR_CLEAR; } break; case fOnDestroy: if (reinterpret_cast(pPage)->m_pData) { PATTR_DATA pData = reinterpret_cast(((CDsTableDrivenPage *)pPage)->m_pData); PVOID pVoid = reinterpret_cast(pData->pVoid); DO_DEL(pVoid); } break; } return S_OK; } //+---------------------------------------------------------------------------- // // Function: DirectReportsList // // Synopsis: Handles the User Organisation Direct Reports list. // //----------------------------------------------------------------------------- HRESULT DirectReportsList(CDsPropPageBase * pPage, PATTR_MAP pAttrMap, PADS_ATTR_INFO pAttrInfo, LPARAM lParam, PATTR_DATA, DLG_OP DlgOp) { // // Multi-select will result in a return at this point // if (pPage->GetObjPathName() == NULL) { return S_OK; } switch (DlgOp) { case fInit: { HRESULT hr; Smart_PADS_ATTR_INFO spAttrs; DWORD cAttrs = 0; PWSTR rgpwzAttrNames[] = {pAttrMap->AttrInfo.pszAttrName}; CComPtr spGcObj; hr = BindToGCcopyOfObj(pPage, pPage->GetObjPathName(), &spGcObj); if (SUCCEEDED(hr)) { hr = spGcObj->GetObjectAttributes(rgpwzAttrNames, 1, &spAttrs, &cAttrs); if (SUCCEEDED(hr)) { // // If the bind to the GC was successful, use those results. // Otherwise, use the results of the local object read. // if (!cAttrs) { return S_OK; } pAttrInfo = spAttrs; } } if (!pAttrInfo) { return S_OK; } for (DWORD i = 0; i < pAttrInfo->dwNumValues; i++) { PWSTR pwzDns; hr = CrackName(pAttrInfo->pADsValues[i].DNString, &pwzDns, GET_OBJ_CAN_NAME_EX, pPage->GetHWnd()); CHECK_HRESULT(hr, return hr); PTSTR ptz, ptzName; if (!UnicodeToTchar(pwzDns, &ptz)) { LocalFreeStringW(&pwzDns); REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); return E_OUTOFMEMORY; } LocalFreeStringW(&pwzDns); ptzName = _tcschr(ptz, TEXT('\n')); dspAssert(ptzName); ptzName++; LRESULT lresult = SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_ADDSTRING, 0, (LPARAM)ptzName); if (lresult != LB_ERR) { PWSTR pwzDN = new WCHAR[wcslen(pAttrInfo->pADsValues[i].DNString) + 1]; if (pwzDN != NULL) { wcscpy(pwzDN, pAttrInfo->pADsValues[i].DNString); SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_SETITEMDATA, lresult, (LPARAM)pwzDN); } } delete ptz; } } break; case fOnCommand: { if (lParam == LBN_DBLCLK) { // // Retrieve the current selection // PWSTR pwzDN = NULL; LRESULT lresult = SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_GETCURSEL, 0, 0); if (lresult != LB_ERR) { // // Get the DN associated with the item // lresult = SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_GETITEMDATA, lresult, 0); if (lresult != LB_ERR) { pwzDN = (PWSTR)lresult; if (pwzDN != NULL) { // // Launch the secondary proppages // PostPropSheet(pwzDN, pPage); } } } } } break; case fOnDestroy: { // // Must free the memory associated with the list box // LRESULT lresult = SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_GETCOUNT, 0, 0); if (lresult != LB_ERR) { for (LRESULT idx = lresult - 1 ; idx >= 0; idx--) { lresult = SendDlgItemMessage(pPage->GetHWnd(), IDC_REPORTS_LIST, LB_GETITEMDATA, idx, 0); if (lresult != LB_ERR) { PWSTR pwzDN = (PWSTR)lresult; if (pwzDN != NULL) { delete[] pwzDN; } } } } } break; default: break; } return S_OK; }