//+---------------------------------------------------------------------------- // // Windows NT Directory Service Property Pages // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1999 // // File: proputil.cxx // // Contents: CDsPropPagesHost IUnknown and ClassFactory, CDsPropDataObj. // // History: 21-March-97 EricB created // //----------------------------------------------------------------------------- #include "pch.h" #include "proppage.h" #include "notify.h" #include // DS Admin definition of PPROPSHEETCFG #include #include // SHCreatePropertyBag() // // This CLSID for the Domain Tree snapin is copied from cdomain.cpp. // const CLSID CLSID_DomainAdmin = { /* ebc53a38-a23f-11d0-b09b-00c04fd8dca6 */ 0xebc53a38, 0xa23f, 0x11d0, {0xb0, 0x9b, 0x00, 0xc0, 0x4f, 0xd8, 0xdc, 0xa6} }; //BOOL CALLBACK AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall); HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, HWND hNotifyObj, BOOL fReadOnly); HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, BOOL fReadOnly); //+---------------------------------------------------------------------------- // // CDsPropPagesHost IUnknown methods // //----------------------------------------------------------------------------- //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHost::IUnknown::QueryInterface // // Synopsis: Returns requested interface pointer // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropPagesHost::QueryInterface(REFIID riid, void ** ppvObject) { TRACE2(CDsPropPagesHost,QueryInterface); if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)(LPSHELLEXTINIT)this; } else if (IID_IShellExtInit == riid) { *ppvObject = (LPSHELLEXTINIT)this; } else if (IID_IShellPropSheetExt == riid) { *ppvObject = (LPSHELLPROPSHEETEXT)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHost::IUnknown::AddRef // // Synopsis: increments reference count // // Returns: the reference count // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropPagesHost::AddRef(void) { dspDebugOut((DEB_USER2, "CDsPropPagesHost::AddRef refcount going in %d\n", m_uRefs)); return InterlockedIncrement((long *)&m_uRefs); } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHost::IUnknown::Release // // Synopsis: Decrements the object's reference count and frees it when // no longer referenced. // // Returns: zero if the reference count is zero or non-zero otherwise // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropPagesHost::Release(void) { dspDebugOut((DEB_USER2, "CDsPropPagesHost::Release ref count going in %d\n", m_uRefs)); unsigned long uTmp; if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0) { delete this; } return uTmp; } //+---------------------------------------------------------------------------- // // CDsPropPagesHostCF - class factory for the CDsPropPagesHost object // //----------------------------------------------------------------------------- //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::Create // // Synopsis: creates a new class factory object // //----------------------------------------------------------------------------- IClassFactory * CDsPropPagesHostCF::Create(PDSCLASSPAGES pDsPP) { return new CDsPropPagesHostCF(pDsPP); } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::CDsPropPagesHostCF // // Synopsis: ctor // //----------------------------------------------------------------------------- CDsPropPagesHostCF::CDsPropPagesHostCF(PDSCLASSPAGES pDsPP) : m_pDsPP(pDsPP), m_uRefs(1) { TRACE2(CDsPropPagesHostCF,CDsPropPagesHostCF); #ifdef _DEBUG strcpy(szClass, "CDsPropPagesHostCF"); #endif } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::~CDsPropPagesHostCF // // Synopsis: dtor // //----------------------------------------------------------------------------- CDsPropPagesHostCF::~CDsPropPagesHostCF(void) { TRACE2(CDsPropPagesHostCF,~CDsPropPagesHostCF); } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::IUnknown::QueryInterface // // Synopsis: Returns requested interface pointer // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropPagesHostCF::QueryInterface(REFIID riid, void ** ppvObject) { dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::QueryInterface\n")); if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)this; } else if (IsEqualIID(IID_IClassFactory, riid)) { *ppvObject = (IClassFactory *)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::IUnknown::AddRef // // Synopsis: increments reference count // // Returns: the new reference count // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropPagesHostCF::AddRef(void) { dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::AddRef refcount going in %d\n", m_uRefs)); return InterlockedIncrement((long *)&m_uRefs); } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::IUnknown::Release // // Synopsis: decrement the refcount // // Returns: the new reference count // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropPagesHostCF::Release(void) { dspDebugOut((DEB_USER2, "CDsPropPagesHostCF::Release ref count going in %d\n", m_uRefs)); unsigned long uTmp; if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0) { delete this; } return uTmp; } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::IClassFactory::CreateInstance // // Synopsis: create an incore instance of the proppage host class object // // Arguments: [pUnkOuter] - aggregator // [riid] - requested interface // [ppvObject] - receptor for itf ptr // // Returns: HRESULTS // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropPagesHostCF::CreateInstance(IUnknown*, REFIID riid, void **ppvObject) { TRACE2(CDsPropPagesHostCF,CreateInstance); HRESULT hr = S_OK; *ppvObject = NULL; CDsPropPagesHost * pPropPage = new CDsPropPagesHost(m_pDsPP); if (pPropPage == NULL) { return E_OUTOFMEMORY; } hr = pPropPage->QueryInterface(riid, ppvObject); if (FAILED(hr)) { ERR_OUT("CDsPropPagesHostCF::CreateInstance, pPropPage->QueryInterface", hr); pPropPage->Release(); return hr; } // // We got a refcount of one when launched, and the above QI increments it // to 2, so call release to take it back to 1. // pPropPage->Release(); return hr; } //+---------------------------------------------------------------------------- // // Member: CDsPropPagesHostCF::IClassFactory::LockServer // // Synopsis: Called with fLock set to TRUE to indicate that the server // should continue to run even if none of its objects are active // // Arguments: [fLock] - increment/decrement the instance count // // Returns: HRESULTS // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropPagesHostCF::LockServer(BOOL fLock) { CDll::LockServer(fLock); return S_OK; } //+---------------------------------------------------------------------------- // // Function: AllocWStr // // Synopsis: Creates a copy of the passed in string. Allocates memory for // the returned string using new. Callers must free the string // memory when done using delete. // // Returns: FALSE for out of memory failures. // //----------------------------------------------------------------------------- BOOL AllocWStr(PWSTR pwzStrIn, PWSTR * ppwzNewStr) { if (pwzStrIn == NULL) { *ppwzNewStr = NULL; return TRUE; } *ppwzNewStr = new WCHAR[wcslen(pwzStrIn) + 1]; if (NULL == *ppwzNewStr) { return FALSE; } wcscpy(*ppwzNewStr, pwzStrIn); return TRUE; } //+---------------------------------------------------------------------------- // // Function: AllocTStr // // Synopsis: Creates a copy of the passed in string. Allocates memory for // the returned string using new. Callers must free the string // memory when done using delete. // // Returns: FALSE for out of memory failures. // //----------------------------------------------------------------------------- BOOL AllocTStr(PTSTR ptzStrIn, PTSTR * pptzNewStr) { *pptzNewStr = new TCHAR[_tcslen(ptzStrIn) + 1]; if (NULL == *pptzNewStr) { return FALSE; } _tcscpy(*pptzNewStr, ptzStrIn); return TRUE; } //+---------------------------------------------------------------------------- // // Function: UnicodeToTchar // // Synopsis: Converts a Unicode string to a TCHAR string. Allocates memory // for the returned string using new. Callers must free the // string memory when done using delete. // // Returns: FALSE for out of memory failures. // //----------------------------------------------------------------------------- BOOL UnicodeToTchar(LPWSTR pwszIn, LPTSTR * pptszOut) { size_t len; #ifdef UNICODE len = wcslen(pwszIn); #else len = WideCharToMultiByte(CP_ACP, 0, pwszIn, -1, NULL, 0, NULL, NULL); #endif //NTRAID#NTBUG9-572008-2002/03/10-jmessec Length calculation, for correctness, should not use //mixed metaphor of char length and byte length (if UNICODE, len == cch, else len == byte length of buffer (including NULL) *pptszOut = new TCHAR[len + 1]; CHECK_NULL(*pptszOut, return FALSE); #ifdef UNICODE wcscpy(*pptszOut, pwszIn); #else if (WideCharToMultiByte(CP_ACP, 0, pwszIn, -1, *pptszOut, len, NULL, NULL) == 0) { delete [] *pptszOut; return FALSE; } #endif return TRUE; } //+---------------------------------------------------------------------------- // // Function: TcharToUnicode // // Synopsis: Converts a TCHAR string to a Unicode string. Allocates memory // for the returned string using new. Callers must free the // string memory when done using delete. // // Returns: FALSE for out of memory failures. // //----------------------------------------------------------------------------- BOOL TcharToUnicode(LPTSTR ptszIn, LPWSTR * ppwszOut) { size_t len; #ifdef UNICODE len = wcslen(ptszIn); #else len = MultiByteToWideChar(CP_ACP, 0, ptszIn, -1, NULL, 0); #endif *ppwszOut = new WCHAR[len + 1]; CHECK_NULL(*ppwszOut, return FALSE); #ifdef UNICODE wcscpy(*ppwszOut, ptszIn); #else if (MultiByteToWideChar(CP_ACP, 0, ptszIn, -1, *ppwszOut, len) == 0) { delete [] *ppwszOut; return FALSE; } #endif return TRUE; } #ifdef _PROVIDE_CFSTR_SHELLIDLIST_FORMAT // FROM DIZ (dsuiext\query.cpp) //#if DELEGATE //WCHAR c_szDsMagicPath[] = L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}"; //#else WCHAR c_szDsMagicPath[] = L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork\\::{fe1290f0-cfbd-11cf-a330-00aa00c16e65}"; //#endif #if !DOWNLEVEL_SHELL /*----------------------------------------------------------------------------- / BindToPath / ---------- / Given a namespace path bind to it returning the shell object / / In: / pszPath -> path to bind to / riid = interface to request / ppvObject -> receives the object pointer / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT BindToPath(LPWSTR pszPath, REFIID riid, LPVOID* ppObject) { HRESULT hres; IShellFolder* psfDesktop = NULL; LPITEMIDLIST pidl = NULL; //TraceEnter(TRACE_VIEW, "BindToPath"); hres = CoCreateInstance(CLSID_ShellDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&psfDesktop); if (FAILED(hres)) { //FailGracefully(hres, "Failed to get IShellFolder for the desktop object"); goto exit_gracefully; } hres = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL); if (FAILED(hres)) { //FailGracefully(hres, "Failed when getting root path of DS"); goto exit_gracefully; } if ( ILIsEmpty(pidl) ) { //TraceMsg("PIDL is desktop, therefore just QIing for interface"); hres = psfDesktop->QueryInterface(riid, ppObject); } else { //TraceMsg("Binding to IDLIST via BindToObject"); hres = psfDesktop->BindToObject(pidl, NULL, riid, ppObject); } exit_gracefully: if ( FAILED(hres) ) *ppObject = NULL; if (psfDesktop) psfDesktop->Release(); ILFree(pidl); pidl = NULL; return hres; } #endif HRESULT _GetDirectorySF(IShellFolder **ppsf) { HRESULT hres; IShellFolder *psf = NULL; IDsFolderInternalAPI *pdfi = NULL; #if DOWNLEVEL_SHELL IPersistFolder* ppf = NULL; #endif DWORD _dwFlags = 0; //TraceEnter(TRACE_VIEW, "CDsQuery::GetDirectorySF"); #if !DOWNLEVEL_SHELL // just bind to the path if this is not a downlevel shell. hres = BindToPath(c_szDsMagicPath, IID_IShellFolder, (void **)&psf); if (FAILED(hres)) { //FailGracefully(hres, "Failed to get IShellFolder view of the DS namespace"); goto exit_gracefully; } #else // on the downlevel shell we need to CoCreate the IShellFolder implementation for the // DS namespace and initialize it manually hres = CoCreateInstance(CLSID_MicrosoftDS, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (void **)&psf); if (FAILED(hres)) { //FailGracefully(hres, "Failed to get the IShellFolder interface we need"); goto exit_gracefully; } if ( SUCCEEDED(psf->QueryInterface(IID_IPersistFolder, (void **)&ppf)) ) { ITEMIDLIST idl = { 0 }; ppf->Initialize(&idl); // it calls ILCLone, so we are safe with stack stuff ppf->Release(); } #endif // using the internal API set all the parameters for dsfolder hres = psf->QueryInterface(IID_IDsFolderInternalAPI, (void **)&pdfi); if (FAILED(hres)) { //FailGracefully(hres, "Failed to get the IDsFolderInternalAPI"); goto exit_gracefully; } pdfi->SetAttributePrefix((_dwFlags & DSQPF_ENABLEADMINFEATURES) ? DS_PROP_ADMIN_PREFIX:NULL); if ( _dwFlags & DSQPF_ENABLEADVANCEDFEATURES ) pdfi->SetProviderFlags(~DSPROVIDER_ADVANCED, DSPROVIDER_ADVANCED); #if (FALSE) hres = pdfi->SetComputer(_pServer, _pUserName, _pPassword); //hres = pdfi->SetComputer(L"marcoc201-50.marcocdev.nttest.microsoft.com", NULL, NULL); if (FAILED(hres)) { //FailGracefully(hres, "Failed when setting credential information to be used"); goto exit_gracefully; } #endif exit_gracefully: if ( SUCCEEDED(hres) ) psf->QueryInterface(IID_IShellFolder, (void **)ppsf); if (psf) psf->Release(); if (pdfi) pdfi->Release(); return hres; } /*----------------------------------------------------------------------------- / CDsQuery::_ADsPathToIdList / -------------------------- / Convert an ADsPath into an IDLIST that is suitable for the DS ShellFolder / implementation. / / In: / ppidl -> receives the resulting IDLIST / pPath = name to be parsed / = NULL then generate only the root of the namespace / pObjectClass = class to use / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT _ADsPathToIdList(LPITEMIDLIST* ppidl, LPWSTR pPath, LPWSTR pObjectClass) { HRESULT hres; IBindCtx *pbc = NULL; IPropertyBag *ppb = NULL; IShellFolder *psf = NULL; VARIANT var; USES_CONVERSION; *ppidl = NULL; // incase we fail // if we have an object class then create a bind context containing it if ( pObjectClass ) { // create a property bag hres = ::SHCreatePropertyBag(IID_IPropertyBag, (void **)&ppb); if (FAILED(hres)) { //FailGracefully(hres, "Failed to create a property bag"); goto exit_gracefully; } // allocate a variant BSTR and set it V_VT(&var) = VT_BSTR; V_BSTR(&var) = ::SysAllocString(pObjectClass); if ( V_BSTR(&var) ) { ppb->Write(DS_PDN_OBJECTLCASS, &var); ::VariantClear(&var); } hres = CreateBindCtx(0, &pbc); if (FAILED(hres)) { //FailGracefully(hres, "Failed to create the BindCtx object"); goto exit_gracefully; } hres = pbc->RegisterObjectParam(DS_PDN_PROPERTYBAG, ppb); if (FAILED(hres)) { //FailGracefully(hres, "Failed to register the property bag with bindctx"); goto exit_gracefully; } } hres = _GetDirectorySF(&psf); if (FAILED(hres)) { //FailGracefully(hres, "Failed to get ShellFolder for DS namespace"); goto exit_gracefully; } hres = psf->ParseDisplayName(NULL, pbc, pPath, NULL, ppidl, NULL); if (FAILED(hres)) { //FailGracefully(hres, "Failed to parse the name"); goto exit_gracefully; } exit_gracefully: if (pbc) pbc->Release(); if (ppb) ppb->Release(); if (psf) psf->Release(); return hres; } #endif // _PROVIDE_CFSTR_SHELLIDLIST_FORMAT //+---------------------------------------------------------------------------- // // Class: CDsPropDataObj // //----------------------------------------------------------------------------- CDsPropDataObj::CDsPropDataObj(HWND hwndParentPage, BOOL fReadOnly) : m_fReadOnly(fReadOnly), m_pwszObjName(NULL), m_pwszObjClass(NULL), m_hwndParent(hwndParentPage), m_uRefs(1), m_pSelectionList(NULL) { #ifdef _DEBUG strcpy(szClass, "CDsPropDataObj"); #endif dspDebugOut((DEB_USER15, "dsprop's CDsPropDataObj::CDsPropDataObj 0x%x\n", this)); } CDsPropDataObj::~CDsPropDataObj(void) { if (m_pwszObjName) { delete [] m_pwszObjName; } if (m_pwszObjClass) { delete [] m_pwszObjClass; } if (m_pSelectionList != NULL) { for (ULONG idx = 0; idx < m_pSelectionList->cItems; idx++) { if (m_pSelectionList->aDsSelection[idx].pwzName != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzName; m_pSelectionList->aDsSelection[idx].pwzName = NULL; } if (m_pSelectionList->aDsSelection[idx].pwzADsPath != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzADsPath; m_pSelectionList->aDsSelection[idx].pwzADsPath = NULL; } if (m_pSelectionList->aDsSelection[idx].pwzClass != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzClass; m_pSelectionList->aDsSelection[idx].pwzClass = NULL; } } delete m_pSelectionList; m_pSelectionList = NULL; } } //+---------------------------------------------------------------------------- // // Method: CDsPropDataObj::Init // // Synopsis: Second phase of object creation where failable operations are // performed. // //----------------------------------------------------------------------------- HRESULT CDsPropDataObj::Init(LPWSTR pwszObjName, LPWSTR pwszClass) { if (!pwszObjName || *pwszObjName == L'\0') { return E_INVALIDARG; } if (!pwszClass || *pwszClass == L'\0') { return E_INVALIDARG; } m_pwszObjName = new WCHAR[wcslen(pwszObjName) + 1]; CHECK_NULL(m_pwszObjName, return E_OUTOFMEMORY); wcscpy(m_pwszObjName, pwszObjName); m_pwszObjClass = new WCHAR[wcslen(pwszClass) + 1]; CHECK_NULL(m_pwszObjClass, return E_OUTOFMEMORY); wcscpy(m_pwszObjClass, pwszClass); return S_OK; } //+---------------------------------------------------------------------------- // // Method: CDsPropDataObj::Init // // Synopsis: Second phase of object creation where failable operations are // performed. // //----------------------------------------------------------------------------- HRESULT CDsPropDataObj::Init(PDS_SELECTION_LIST pSelectionList) { HRESULT hr = S_OK; if (m_pSelectionList != NULL) { for (ULONG idx = 0; idx < m_pSelectionList->cItems; idx++) { if (m_pSelectionList->aDsSelection[idx].pwzName != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzName; m_pSelectionList->aDsSelection[idx].pwzName = NULL; } if (m_pSelectionList->aDsSelection[idx].pwzADsPath != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzADsPath; m_pSelectionList->aDsSelection[idx].pwzADsPath = NULL; } if (m_pSelectionList->aDsSelection[idx].pwzClass != NULL) { delete[] m_pSelectionList->aDsSelection[idx].pwzClass; m_pSelectionList->aDsSelection[idx].pwzClass = NULL; } } delete m_pSelectionList; m_pSelectionList = NULL; } m_pSelectionList = pSelectionList; return hr; } //+---------------------------------------------------------------------------- // // Method: CDsPropDataObj::IDataObject::GetData // // Synopsis: Returns data, in this case the Prop Page format data. // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropDataObj::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium) { TRACE(CDsPropDataObj,GetData); if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM))) { return E_INVALIDARG; } if (!(pFormatEtc->tymed & TYMED_HGLOBAL)) { return DV_E_TYMED; } if (pFormatEtc->cfFormat == g_cfDsObjectNames) { // Return the object name and class. // size_t cbPath = sizeof(WCHAR) * (wcslen(m_pwszObjName) + 1); size_t cbClass = sizeof(WCHAR) * (wcslen(m_pwszObjClass) + 1); size_t cbStruct = sizeof(DSOBJECTNAMES); LPDSOBJECTNAMES pDSObj; pDSObj = (LPDSOBJECTNAMES)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cbStruct + cbPath + cbClass); if (pDSObj == NULL) { return STG_E_MEDIUMFULL; } pDSObj->clsidNamespace = CLSID_MicrosoftDS; pDSObj->cItems = 1; pDSObj->aObjects[0].offsetName = static_cast(cbStruct); pDSObj->aObjects[0].offsetClass = static_cast(cbStruct + cbPath); if (m_fReadOnly) { pDSObj->aObjects[0].dwFlags = DSOBJECT_READONLYPAGES; } pDSObj->aObjects[0].dwProviderFlags = 0; dspDebugOut((DEB_ITRACE, "returning %ws and %ws\n", m_pwszObjName, m_pwszObjClass)); wcscpy((PWSTR)((BYTE *)pDSObj + cbStruct), m_pwszObjName); wcscpy((PWSTR)((BYTE *)pDSObj + cbStruct + cbPath), m_pwszObjClass); pMedium->hGlobal = (HGLOBAL)pDSObj; } else if (pFormatEtc->cfFormat == g_cfDsPropCfg) { // Return the property sheet notification info. In this case, it is // the invokding sheet's hwnd. // PPROPSHEETCFG pSheetCfg; pSheetCfg = (PPROPSHEETCFG)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(PROPSHEETCFG)); if (pSheetCfg == NULL) { return STG_E_MEDIUMFULL; } ZeroMemory(pSheetCfg, sizeof(PROPSHEETCFG)); pSheetCfg->hwndParentSheet = m_hwndParent; pMedium->hGlobal = (HGLOBAL)pSheetCfg; } #ifdef _PROVIDE_CFSTR_SHELLIDLIST_FORMAT else if (pFormatEtc->cfFormat == g_cfShellIDListArray) { // Return the CFSTR_SHELLIDLIST clipboard format LPITEMIDLIST pidl = NULL; HRESULT hr = _ADsPathToIdList(&pidl, m_pwszObjName, m_pwszObjClass); if (FAILED(hr)) { return hr; } if (pidl == NULL) { return E_UNEXPECTED; } // we have a valid pidl, need to allocate global memory // get the count of bytes UINT cBytes = sizeof(CIDA) + ILGetSize(pidl) + sizeof(DWORD); LPIDA pIDArray = (LPIDA)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cBytes); if (pIDArray == NULL) { hr = STG_E_MEDIUMFULL; } else { hr = S_OK; pIDArray->cidl = 1; pIDArray->aoffset[0] = sizeof(CIDA); LPBYTE pMem = ((LPBYTE)pIDArray) + sizeof(CIDA); // copy the pidl past the CIDA ::memcpy(pMem, pidl, ILGetSize(pidl)); pMem += ILGetSize(pidl); // make sure we have a NULL dword past the list ::ZeroMemory(pMem, sizeof(DWORD)); pMedium->hGlobal = (HGLOBAL)pIDArray; } SHILFree(pidl); if (FAILED(hr)) { return hr; } } #endif // _PROVIDE_CFSTR_SHELLIDLIST_FORMAT else if (pFormatEtc->cfFormat == g_cfDsSelList) { // // Return the selection list for ObjectPicker // PDS_SELECTION_LIST pSelectionList = NULL; if (m_pSelectionList != NULL) { size_t cbRequired = sizeof(DS_SELECTION_LIST) + (sizeof(DS_SELECTION) * (m_pSelectionList->cItems - 1)); pSelectionList = (PDS_SELECTION_LIST)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cbRequired); if (pSelectionList == NULL) { return STG_E_MEDIUMFULL; } ZeroMemory(pSelectionList, cbRequired); memcpy(pSelectionList, m_pSelectionList, cbRequired); pMedium->hGlobal = (HGLOBAL)pSelectionList; } else { size_t cbRequired = sizeof(DS_SELECTION_LIST); pSelectionList = (PDS_SELECTION_LIST)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cbRequired); if (pSelectionList == NULL) { return STG_E_MEDIUMFULL; } ZeroMemory(pSelectionList, cbRequired); pMedium->hGlobal = (HGLOBAL)pSelectionList; } } else { return DV_E_FORMATETC; } pMedium->tymed = TYMED_HGLOBAL; pMedium->pUnkForRelease = NULL; return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsPropDataObj::IUnknown::QueryInterface // // Synopsis: Returns requested interface pointer // //----------------------------------------------------------------------------- STDMETHODIMP CDsPropDataObj::QueryInterface(REFIID riid, void ** ppvObject) { TRACE2(CDsPropDataObj,QueryInterface); if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)this; } else if (IID_IDataObject == riid) { *ppvObject = this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsPropDataObj::IUnknown::AddRef // // Synopsis: increments reference count // // Returns: the reference count // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropDataObj::AddRef(void) { dspDebugOut((DEB_USER15, "CDsPropDataObj::AddRef refcount going in %d\n", m_uRefs)); return InterlockedIncrement((long *)&m_uRefs); } //+---------------------------------------------------------------------------- // // Member: CDsPropDataObj::IUnknown::Release // // Synopsis: Decrements the object's reference count and frees it when // no longer referenced. // // Returns: zero if the reference count is zero or non-zero otherwise // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsPropDataObj::Release(void) { dspDebugOut((DEB_USER15, "CDsPropDataObj::Release ref count going in %d\n", m_uRefs)); unsigned long uTmp; if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0) { delete this; } return uTmp; } //+---------------------------------------------------------------------------- // // Function: AddPageProc // // Synopsis: The IShellPropSheetExt->AddPages callback. // //----------------------------------------------------------------------------- //BOOL CALLBACK //AddPageProc(HPROPSHEETPAGE hPage, LPARAM pCall) //{ // HRESULT hr; // hr = ((LPPROPERTYSHEETCALLBACK)pCall)->AddPage(hPage); // return hr == S_OK; //} //+---------------------------------------------------------------------------- // // Function: PostPropSheet, internal; used by CDsPropPageBase members. // // Synopsis: Creates a property sheet for the named object using MMC's // IPropertySheetProvider so that extension snapins can add pages. // // Arguments: [pwzObjDN] - the DN path of the DS object. // [pParentPage] - the invoking page's this pointer. // [fReadOnly] - defaults to FALSE. // //----------------------------------------------------------------------------- HRESULT PostPropSheet(PWSTR pwzObjDN, CDsPropPageBase * pParentPage, BOOL fReadOnly) { HRESULT hr; CComPtr spADsPath; dspDebugOut((DEB_ITRACE, "PostPropSheet passed path %ws\n", pwzObjDN)); hr = pParentPage->GetADsPathname(spADsPath); CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr); hr = spADsPath->Set(CComBSTR(pwzObjDN), ADS_SETTYPE_DN); CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr); hr = spADsPath->put_EscapedMode(ADS_ESCAPEDMODE_ON); CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr); BSTR bstrEscapedPath; hr = spADsPath->Retrieve(ADS_FORMAT_X500_DN, &bstrEscapedPath); CHECK_HRESULT_REPORT(hr, pParentPage->GetHWnd(), return hr); dspDebugOut((DEB_ITRACE, "PostPropSheet escaped path %ws\n", bstrEscapedPath)); hr = PostPropSheetWorker(pParentPage, bstrEscapedPath, pParentPage->m_pWPTDataObj, pParentPage->GetHWnd(), fReadOnly); SysFreeString(bstrEscapedPath); // There is no reason to report an error here because all errors are reported // from within PostPropSheetWorker return S_OK; } //+---------------------------------------------------------------------------- // // Function: PostADsPropSheet, exported from DLL. // // Synopsis: Creates a property sheet for the named object using MMC's // IPropertySheetProvider so that extension snapins can add pages. // // Arguments: [pwzObjDN] - the full LDAP DN of the DS object. // [pParentObj] - the invoking page's MMC data object pointer. // [hwndParent] - the invoking page's window handle. // [fReadOnly] - defaults to FALSE. // //----------------------------------------------------------------------------- HRESULT PostADsPropSheet(PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, BOOL fReadOnly) { return PostPropSheetWorker(NULL, pwzObjDN, pParentObj, hwndParent, fReadOnly); } //+---------------------------------------------------------------------------- // // Function: PostADsPropSheet, exported from DLL. // // Synopsis: Creates a property sheet for the named object using MMC's // IPropertySheetProvider so that extension snapins can add pages. // // Arguments: [pwzObjDN] - the full LDAP DN of the DS object. // [pParentObj] - the invoking page's MMC data object pointer. // [hwndParent] - the invoking page's window handle. // [hNotifyObj] - the handle to the notify object window // [fReadOnly] - defaults to FALSE. // //----------------------------------------------------------------------------- HRESULT PostADsPropSheet(PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, HWND hNotifyObj, BOOL fReadOnly) { return PostPropSheetWorker(NULL, pwzObjDN, pParentObj, hwndParent, hNotifyObj, fReadOnly); } //+---------------------------------------------------------------------------- // // Function: CreateSecondaryPageInfo // // Synopsis: Helper for posting a second sheet. // //----------------------------------------------------------------------------- PDSA_SEC_PAGE_INFO CreateSecondaryPageInfo(HWND hwndParent, LPCWSTR lpszTitle, LPCWSTR lpszName, LPCWSTR lpszClass, BOOL fReadOnly) { // determine memory allocation needs size_t nTitleLen = wcslen(lpszTitle)+1; size_t nNameLen = wcslen(lpszName)+1; size_t nClassLen = wcslen(lpszClass)+1; size_t cbStruct = sizeof(DSA_SEC_PAGE_INFO); size_t cbStrings = (nTitleLen+nNameLen+nClassLen)*sizeof(WCHAR); // allocate memory PDSA_SEC_PAGE_INFO pDsaSecondaryPageInfo = (PDSA_SEC_PAGE_INFO) ::LocalAlloc(LPTR, (cbStruct + cbStrings)); if (pDsaSecondaryPageInfo == NULL) return NULL; // set members pDsaSecondaryPageInfo->hwndParentSheet = hwndParent; // title string just at the end of the DSA_SEC_PAGE_INFO struct pDsaSecondaryPageInfo->offsetTitle = static_cast(cbStruct); wcscpy((LPWSTR)((BYTE *)pDsaSecondaryPageInfo + pDsaSecondaryPageInfo->offsetTitle), lpszTitle); DSOBJECTNAMES* pDsObjectNames = &(pDsaSecondaryPageInfo->dsObjectNames); pDsObjectNames->cItems = 1; DSOBJECT* pDsObject = &(pDsObjectNames->aObjects[0]); pDsObject->dwFlags = fReadOnly ? DSOBJECT_READONLYPAGES : 0; pDsObject->dwProviderFlags = 0; // not set // DSOBJECT strings past the DSA_SEC_PAGE_INFO strings // offsets are relative to the beginning of DSOBJECT struct pDsObject->offsetName = static_cast(sizeof(DSOBJECT) + nTitleLen*sizeof(WCHAR)); wcscpy((LPWSTR)((BYTE *)pDsObject + pDsObject->offsetName), lpszName); pDsObject->offsetClass = static_cast(pDsObject->offsetName + nNameLen*sizeof(WCHAR)); wcscpy((LPWSTR)((BYTE *)pDsObject + pDsObject->offsetClass), lpszClass); return pDsaSecondaryPageInfo; } //+---------------------------------------------------------------------------- // // Function: _WindowsPathFromDN // // Synopsis: Helper to translate a DN into a windows path // for example, from "CN=Joe,OU=test,DN=acme,DN=com" // to "LDAP://DN=com/DN=acme/OU=test/CN=Joe // //----------------------------------------------------------------------------- HRESULT _WindowsPathFromDN(IN PWSTR pwzObjDN, OUT BSTR* pbstrWindowsPath) { // create a path cracker object CComPtr spIADsPathname; HRESULT hr = ::CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (void**)&spIADsPathname); if (FAILED(hr)) { return hr; } spIADsPathname->Set(CComBSTR(pwzObjDN), ADS_SETTYPE_DN); hr = spIADsPathname->Retrieve(ADS_FORMAT_WINDOWS, pbstrWindowsPath); return hr; } //+---------------------------------------------------------------------------- // // Function: PostPropSheetWorker // // Synopsis: Helper for posting a second sheet. // //----------------------------------------------------------------------------- HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, BOOL fReadOnly) { HWND hNotifyObj = NULL; ::SendMessage(hwndParent, WM_ADSPROP_PAGE_GET_NOTIFY, (WPARAM)&hNotifyObj, 0); if (hNotifyObj == NULL) return E_UNEXPECTED; return PostPropSheetWorker(pParentPage, pwzObjDN, pParentObj, hwndParent, hNotifyObj, fReadOnly); } HRESULT PostPropSheetWorker(CDsPropPageBase * pParentPage, PWSTR pwzObjDN, IDataObject * pParentObj, HWND hwndParent, HWND hNotifyObj, BOOL fReadOnly) { HRESULT hr = S_OK; CDsPropDataObj * pDataObj = NULL; BSTR bstrName = NULL, bstrClass = NULL; BSTR bstrPath = NULL; CStrW strADsPath; CWaitCursor cWait; BOOL fShell = FALSE; PWSTR pwszName = NULL; CComPtr spPathCracker; hr = AddLDAPPrefix(pParentPage, pwzObjDN, strADsPath); CHECK_HRESULT(hr, return hr); #ifdef DSADMIN // Check to see if a sheet is already posted for this object. // if (FindSheet(const_cast((LPCWSTR)strADsPath))) { return S_OK; } #endif // DSADMIN // // Activate the object as a test of the path's correctness. Get the // naming attribute, class, and schema class GUID while we are at it. // IADs * pObj = NULL; dspDebugOut((DEB_USER1, "Opening prop sheet on '%S'\n", strADsPath)); hr = DSAdminOpenObject(const_cast((LPCWSTR)strADsPath), IID_IADs, (PVOID *)&pObj); if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { // Display the object-not-found error message. // TCHAR tzFormat[MAX_ERRORMSG+1], tzTitle[MAX_TITLE+1]; PTSTR ptzObjDN, ptzMsg; LOAD_STRING(IDS_INVALID_NAME_FORMAT, tzFormat, MAX_ERRORMSG, return E_OUTOFMEMORY); LOAD_STRING(IDS_MSG_TITLE, tzTitle, MAX_TITLE, return E_OUTOFMEMORY); if (!UnicodeToTchar(pwzObjDN, &ptzObjDN)) { return E_OUTOFMEMORY; } //NTRAID#NTBUG9-571996-2002/03/10-jmessec Can't use _tcslen(tzFormat) for buffer length determination; //%25s could be passed in (resource change, localization, etc), causing a buffer overrun ptzMsg = new TCHAR[_tcslen(ptzObjDN) + _tcslen(tzFormat) + 1]; CHECK_NULL(ptzMsg, delete [] ptzObjDN; return E_OUTOFMEMORY); wsprintf(ptzMsg, tzFormat, ptzObjDN); delete [] ptzObjDN; MessageBox(hwndParent, ptzMsg, tzTitle, MB_ICONEXCLAMATION | MB_OK); goto Cleanup; } if (!CHECK_ADS_HR(&hr, hwndParent)) { goto Cleanup; } hr = pObj->get_ADsPath(&bstrPath); CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); hr = pObj->get_Class(&bstrClass); CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); pObj->Release(); pObj = NULL; hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (PVOID *)&spPathCracker); if (SUCCEEDED(hr)) { hr = spPathCracker->Set(bstrPath, ADS_SETTYPE_FULL); if (SUCCEEDED(hr)) { LONG lEscapeMode = 0; hr = spPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); if (SUCCEEDED(hr)) { hr = spPathCracker->get_EscapedMode(&lEscapeMode); if (SUCCEEDED(hr)) { hr = spPathCracker->put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX); if (SUCCEEDED(hr)) { hr = spPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName); dspAssert(bstrName != NULL); } } } // // Set the display back to full so we don't mess up other instances // hr = spPathCracker->SetDisplayType(ADS_DISPLAY_FULL); hr = spPathCracker->put_EscapedMode(lEscapeMode); } } // // If something went wrong with the path cracker just hack the name // ourselves. // if (bstrName == NULL) { hr = pObj->get_Name(&bstrName); CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); pwszName = wcschr(bstrName, L'='); if (pwszName && (*pwszName != L'\0')) { pwszName++; } else { pwszName = bstrName; } } else { pwszName = bstrName; } bool fDomAdmin = false; // // Are we being called from the shell or from the admin snapin? // if (!pParentObj) { fShell = TRUE; } else { FORMATETC fmte = {g_cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM ObjMedium; PDSDISPLAYSPECOPTIONS pDsDispSpecOptions; PWSTR pwzAttribPrefix; hr = pParentObj->GetData(&fmte, &ObjMedium); if (DV_E_FORMATETC == hr) { fShell = TRUE; } else { CHECK_HRESULT(hr, return hr); pDsDispSpecOptions = (PDSDISPLAYSPECOPTIONS)ObjMedium.hGlobal; pwzAttribPrefix = (PWSTR)ByteOffset(pDsDispSpecOptions, pDsDispSpecOptions->offsetAttribPrefix); if (_wcsicmp(pwzAttribPrefix, DS_PROP_ADMIN_PREFIX) != 0) { fShell = TRUE; } ReleaseStgMedium(&ObjMedium); } fmte.cfFormat = g_cfDsObjectNames; fmte.ptd = NULL; fmte.dwAspect = DVASPECT_CONTENT; fmte.lindex = -1; fmte.tymed = TYMED_HGLOBAL; hr = pParentObj->GetData(&fmte, &ObjMedium); CHECK_HRESULT(hr, return hr); LPDSOBJECTNAMES pDsObjectNames = (LPDSOBJECTNAMES)ObjMedium.hGlobal; if (IsEqualCLSID(pDsObjectNames->clsidNamespace, CLSID_DomainAdmin)) { fDomAdmin = true; } ReleaseStgMedium(&ObjMedium); } if (fShell) { // the shell uses windows paths, so we have to convert the DN // BSTR bstrWindowsPath = NULL; CStrW strADsWindowsPath; hr = _WindowsPathFromDN(pwzObjDN, &bstrWindowsPath); if (SUCCEEDED(hr)) { strADsWindowsPath = bstrWindowsPath; ::SysFreeString(bstrWindowsPath); } else { // if we fail, try to survive with the regular ADSI path strADsWindowsPath = strADsPath; } // try to see it a sheet with this title is already up if (FindSheet(const_cast((LPCWSTR)strADsWindowsPath))) { goto Cleanup; } // Create the intermediary data object. // pDataObj = new CDsPropDataObj(hwndParent, fReadOnly); CHECK_NULL_REPORT(pDataObj, hwndParent, goto Cleanup;); hr = pDataObj->Init(const_cast((LPCWSTR)strADsWindowsPath), bstrClass); CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); // // Have the DS shell folder extension post the sheet. // IDsFolderProperties * pdfp = NULL; hr = CoCreateInstance(CLSID_DsFolderProperties, NULL, CLSCTX_INPROC_SERVER, IID_IDsFolderProperties, (void **)&pdfp); CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); hr = pdfp->ShowProperties(hwndParent, pDataObj); CHECK_HRESULT_REPORT(hr, hwndParent, ;); pdfp->Release(); goto Cleanup; } // called from DS Admin, delegate to it { // // First crack the path to something readable // if (!pwszName) { PWSTR pwzCanEx = NULL; hr = CrackName(pwzObjDN, &pwzCanEx, GET_OBJ_CAN_NAME_EX, hwndParent); if (SUCCEEDED(hr) && pwzCanEx) { PWSTR pwzSlash = wcsrchr(pwzCanEx, L'\n'); if (pwzSlash) { pwszName = pwzSlash + 1; } else { pwszName = pwzCanEx; } } } hr = S_OK; PDSA_SEC_PAGE_INFO pDsaSecondaryPageInfo = CreateSecondaryPageInfo(hwndParent, pwszName, pwzObjDN, bstrClass, fReadOnly); if (pDsaSecondaryPageInfo == NULL) { hr = E_OUTOFMEMORY; CHECK_HRESULT_REPORT(hr, hwndParent, goto Cleanup;); } PWSTR pwzDC = NULL; if (fDomAdmin) { // // Get the DC name to send to the snap in. // CStrW strDC; hr = GetLdapServerName(pParentPage->m_pDsObj, strDC); if (SUCCEEDED(hr)) { pwzDC = (PWSTR) LocalAlloc(LPTR, (strDC.GetLength() + 1) * sizeof(WCHAR)); CHECK_NULL_REPORT(pwzDC, hwndParent, goto Cleanup); wcscpy(pwzDC, strDC); } } ::SendMessage(hNotifyObj, WM_ADSPROP_SHEET_CREATE, (WPARAM)pDsaSecondaryPageInfo, (LPARAM)pwzDC); } cWait.SetOld(); Cleanup: if (pObj) { pObj->Release(); } if (pDataObj) { // The prop sheet holds on to the dataobj, so don't delete it here. // dspDebugOut((DEB_USER15, "PostPropSheet releasing data obj 0x%x\n", pDataObj)); pDataObj->Release(); } if (bstrName) { SysFreeString(bstrName); } if (bstrClass) { SysFreeString(bstrClass); } return hr; } //+---------------------------------------------------------------------------- // // Function: GetDomainScope // // Synopsis: Returns the full LDAP DN of the domain of the current object. // //----------------------------------------------------------------------------- HRESULT GetDomainScope(CDsPropPageBase * pPage, BSTR * pBstrOut) { PWSTR pwzObjDN; HRESULT hr = pPage->SkipPrefix(pPage->GetObjPathName(), &pwzObjDN); CHECK_HRESULT(hr, return hr); hr = GetObjectsDomain(pPage, pwzObjDN, pBstrOut); DO_DEL(pwzObjDN); CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr); return S_OK; } //+---------------------------------------------------------------------------- // // Function: GetObjectsDomain // // Synopsis: Returns the full LDAP DN of the domain of the passed in object. // //----------------------------------------------------------------------------- HRESULT GetObjectsDomain(CDsPropPageBase * pPage, PWSTR pwzObjDN, BSTR * pBstrOut) { PWSTR pwzDomainDN; HRESULT hr; hr = CrackName(pwzObjDN, &pwzDomainDN, GET_FQDN_DOMAIN_NAME, pPage->GetHWnd()); CHECK_HRESULT(hr, return hr); CComPtr spPathCracker; hr = pPage->GetADsPathname(spPathCracker); CHECK_HRESULT(hr, return hr); hr = spPathCracker->Set(CComBSTR(g_wzLDAPProvider), ADS_SETTYPE_PROVIDER); CHECK_HRESULT(hr, return hr); CStrW strDC; hr = GetLdapServerName(pPage->m_pDsObj, strDC); CHECK_HRESULT(hr, return hr); hr = spPathCracker->Set(CComBSTR(strDC), ADS_SETTYPE_SERVER); CHECK_HRESULT(hr, return hr); hr = spPathCracker->Set(CComBSTR(pwzDomainDN), ADS_SETTYPE_DN); LocalFreeStringW(&pwzDomainDN); CHECK_HRESULT(hr, return hr); hr = spPathCracker->Retrieve(ADS_FORMAT_X500, pBstrOut); CHECK_HRESULT(hr, return hr); dspDebugOut((DEB_USER1, "Object's domain is: '%S'\n", *pBstrOut)); return S_OK; } //+---------------------------------------------------------------------------- // // Function: CheckRegisterClipFormats // // Synopsis: If the clipboard formats haven't been registered yet, register // them. // //----------------------------------------------------------------------------- HRESULT CheckRegisterClipFormats(void) { // Check to see if we have our clipboard formats registered, if not then // lets do it. if (!g_cfDsObjectNames) { g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES); } if (!g_cfDsObjectNames) { ERR_OUT("No clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } if (!g_cfDsDispSpecOptions) { g_cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_DISPLAY_SPEC_OPTIONS); } if (!g_cfDsDispSpecOptions) { ERR_OUT("No DS Display Spec Options clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } if (!g_cfShellIDListArray) { g_cfShellIDListArray = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST); } if (!g_cfShellIDListArray) { ERR_OUT("No Shell IDList Array clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } #ifdef DSADMIN if (!g_cfMMCGetNodeType) { g_cfMMCGetNodeType = (CLIPFORMAT)RegisterClipboardFormat(CCF_NODETYPE); } if (!g_cfMMCGetNodeType) { ERR_OUT("No node-type clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } if (!g_cfDsPropCfg) { g_cfDsPropCfg = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_PROPSHEETCONFIG); } if (!g_cfDsPropCfg) { ERR_OUT("No propsheet cfg clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } if (!g_cfDsSelList) { g_cfDsSelList = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST); } if (!g_cfDsSelList) { ERR_OUT("No object picker clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } if (!g_cfDsMultiSelectProppages) { g_cfDsMultiSelectProppages = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_MULTISELECTPROPPAGE); } if (!g_cfDsMultiSelectProppages) { ERR_OUT("No multi-select proppages clipboard format registered", 0); return HRESULT_FROM_WIN32(GetLastError()); } //if (!g_cfMMCGetCoClass) //{ // g_cfMMCGetCoClass = (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_CLASSID); //} //if (!g_cfMMCGetCoClass) //{ // ERR_OUT("No clipboard format registered", 0); // return HRESULT_FROM_WIN32(GetLastError()); //} #endif return S_OK; } //+---------------------------------------------------------------------------- // // Function: CrackName // // Synopsis: Given an object name, returns the requested name. The string // allocated to hold this name must be freed using // LocalFreeStringW (it and LocalAllocStringW are imported from // dsuiext.dll). // // Operations: // GET_OBJ_CAN_NAME - return the object's canonical name. // GET_OBJ_CAN_NAME_EX - leaf element delimited by CR character. // GET_OBJ_1779_DN - return object's FQDN. // GET_OBJ_NT4_NAME - return object's downlevel name (domain\name). // GET_DNS_DOMAIN_NAME - return object's DNS domain name. // GET_NT4_DOMAIN_NAME - return object's NT4 domain name. // GET_FQDN_DOMAIN_NAME - return object's FQDN domain name. // GET_OBJ_UPN - return object's User Principal name. // // Assumptions: Object (pwzNameIn) can be in any domain. Therefore, resolving // the name may require chasing referrals. // Syntactical-only name cracking is much faster, so don't bind // to the DC unless syntactical-only fails. // // Methodology: // GET_OBJ_CAN_NAME - Try syntactical-only first. If that fails, go through // the loop again using the DC. Format requested is DS_CANONICAL_NAME. // GET_OBJ_CAN_NAME_EX - Same as above except name format requested is // DS_CANONICAL_NAME_EX. // GET_OBJ_1779_DN - Same as above except name format requested is // DS_FQDN_1779_NAME. // GET_OBJ_NT4_NAME = Same as above except name format requested is // DS_NT4_ACCOUNT_NAME (flat-domain\name). // GET_DNS_DOMAIN_NAME - Same as above except return the pDomain element of // the DS_NAME_RESULT struct. // GET_NT4_DOMAIN_NAME - The NT4 domain name is returned by DsGetDcName. // However, we don't know up front whether pwzNameIn is in the current // domain or not. Thus, the goal when the loop is first entered will be // to obtain a valid DNS domain name. That is, when DsCrackNames returns // with a zero status then pwzDomain will contain the DNS domain name for // the object's domain. After that, the DomainName value returned by // DsGetDcName will be the downlevel domain name. // GET_FQDN_DOMAIN_NAME - This is similar to GET_NT4_DOMAIN_NAME except that // once the DNS domain name is obtained, DsCrackNames must be called once // more to convert that to the 1779 name for the domain. // //----------------------------------------------------------------------------- HRESULT CrackName(PWSTR pwzNameIn, PWSTR * ppwzResultName, CRACK_NAME_OPR RequestedOpr, HWND hWnd) { HRESULT hr = S_OK; CRACK_NAME_OPR CurOpr = RequestedOpr; HANDLE hDS = (HANDLE)-1; PDOMAIN_CONTROLLER_INFOW pDCInfo = NULL; DWORD dwErr = 0; PDS_NAME_RESULTW pDsNameResult = NULL; PWSTR * ppwsIn = &pwzNameIn, pwzNameResult = NULL; PWSTR pwzDomain = NULL, pwzNT4Domain = NULL, pwzDomRoot = NULL; BOOL fLoopAgain = FALSE, fGotDnsDomain = FALSE, fNeedDcBind = FALSE; ULONG GetDcFlags = DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED | DS_WRITABLE_REQUIRED; DS_NAME_FORMAT FormatOffered = DS_FQDN_1779_NAME; DS_NAME_FORMAT FormatRequested = DS_CANONICAL_NAME, OriginalFormatRequested = DS_CANONICAL_NAME; DS_NAME_FLAGS NameFlags = DS_NAME_FLAG_SYNTACTICAL_ONLY; dspDebugOut((DEB_USER1, "CrackName input: %ws\n", pwzNameIn)); switch (RequestedOpr) { case GET_NT4_DOMAIN_NAME: case GET_FQDN_DOMAIN_NAME: CurOpr = GET_DNS_DOMAIN_NAME; break; case GET_OBJ_1779_DN: FormatOffered = DS_UNKNOWN_NAME; FormatRequested = DS_FQDN_1779_NAME; break; case GET_OBJ_NT4_NAME: FormatRequested = DS_NT4_ACCOUNT_NAME; break; case GET_OBJ_CAN_NAME_EX: FormatRequested = DS_CANONICAL_NAME_EX; break; case GET_OBJ_UPN: FormatOffered = DS_UNKNOWN_NAME; FormatRequested = DS_USER_PRINCIPAL_NAME; break; } do { if (!(NameFlags & DS_NAME_FLAG_SYNTACTICAL_ONLY)) { // Get a DC name for the domain and bind to it. // if (CurOpr == GET_NT4_DOMAIN_NAME) { GetDcFlags |= DS_RETURN_FLAT_NAME; } dspDebugOut((DEB_ITRACE, "Getting DC for domain '%ws'\n", pwzDomain)); dwErr = DsGetDcNameW(NULL, pwzDomain, NULL, NULL, GetDcFlags, &pDCInfo); CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr)); dspAssert(pDCInfo != NULL); if (CurOpr == GET_NT4_DOMAIN_NAME) { if (FAILED(LocalAllocStringW(&pwzNT4Domain, pDCInfo->DomainName))) { REPORT_ERROR(E_OUTOFMEMORY, hWnd); return E_OUTOFMEMORY; } } else { dwErr = DsBindW(pDCInfo->DomainControllerName, NULL, &hDS); } #ifdef DSADMIN NetApiBufferFree(pDCInfo); #else LocalFree(pDCInfo); #endif pDCInfo = NULL; if (dwErr != ERROR_SUCCESS) { // Try again, the DC returned above was unavailable (i.e., the // cache list was stale). // GetDcFlags |= DS_FORCE_REDISCOVERY; dwErr = DsGetDcNameW(NULL, pwzDomain, NULL, NULL, GetDcFlags, &pDCInfo); CHECK_WIN32_REPORT(dwErr, hWnd, ;); if (dwErr == ERROR_SUCCESS) { dspAssert(pDCInfo != NULL); if (CurOpr == GET_NT4_DOMAIN_NAME) { LocalFreeStringW(&pwzNT4Domain); if (FAILED(LocalAllocStringW(&pwzNT4Domain, pDCInfo->DomainName))) { REPORT_ERROR(E_OUTOFMEMORY, hWnd); return E_OUTOFMEMORY; } } else { dwErr = DsBindW(pDCInfo->DomainControllerName, NULL, &hDS); } } #ifdef DSADMIN NetApiBufferFree(pDCInfo); #else LocalFree(pDCInfo); #endif } CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr)); if (CurOpr == GET_NT4_DOMAIN_NAME) { break; } } // // Convert the object name. // dwErr = DsCrackNamesW(hDS, NameFlags, FormatOffered, FormatRequested, 1, ppwsIn, &pDsNameResult); if (!(NameFlags & DS_NAME_FLAG_SYNTACTICAL_ONLY)) { DsUnBind(&hDS); } CHECK_WIN32_REPORT(dwErr, hWnd, return HRESULT_FROM_WIN32(dwErr)); dspAssert(pDsNameResult); dspAssert(pDsNameResult->cItems == 1); switch (pDsNameResult->rItems->status) { case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING: // // A syntactical-only cracking failed, so do it again and use // the DC. // if (fNeedDcBind) { // Can't syntactically get the domain name, format offered // must not be 1779; nothing we can do. // fLoopAgain = FALSE; fNeedDcBind = FALSE; pwzNameResult = pwzNameIn; hr = MAKE_HRESULT(SEVERITY_ERROR, 0, DS_NAME_ERROR_NO_MAPPING); } else { if (DS_FQDN_1779_NAME == FormatOffered) { // If the format offered is 1779, then a syntactic-only // crack for canonical name will supply the DNS domain name. // So, set a flag that says we are getting the domain name. // fNeedDcBind = TRUE; OriginalFormatRequested = FormatRequested; FormatRequested = DS_CANONICAL_NAME; // // The DS_NAME_FLAG_PRIVATE_PURE_SYNTACTIC flag means to // return the syntactic mapping even for FPOs which other- // wise cannot be cracked syntactically. // NameFlags = (DS_NAME_FLAGS)((int)DS_NAME_FLAG_SYNTACTICAL_ONLY | DS_NAME_FLAG_PRIVATE_PURE_SYNTACTIC); } else { NameFlags = DS_NAME_NO_FLAGS; } fLoopAgain = TRUE; } continue; case DS_NAME_ERROR_DOMAIN_ONLY: // // The object info is in another domain. Go there and try again // (unless only the DNS domain name is needed). // LocalFreeStringW(&pwzDomain); if (FAILED(LocalAllocStringW(&pwzDomain, pDsNameResult->rItems->pDomain))) { REPORT_ERROR(E_OUTOFMEMORY, hWnd); DsFreeNameResultW(pDsNameResult); return E_OUTOFMEMORY; } DsFreeNameResultW(pDsNameResult); pDsNameResult = NULL; fGotDnsDomain = TRUE; if (RequestedOpr == GET_DNS_DOMAIN_NAME) { fLoopAgain = FALSE; } else { NameFlags = DS_NAME_NO_FLAGS; fLoopAgain = TRUE; } break; case DS_NAME_NO_ERROR: // // Success! // fLoopAgain = FALSE; if (CurOpr == GET_DNS_DOMAIN_NAME || fNeedDcBind) { fGotDnsDomain = TRUE; LocalFreeStringW(&pwzDomain); if (FAILED(LocalAllocStringW(&pwzDomain, pDsNameResult->rItems->pDomain))) { REPORT_ERROR(E_OUTOFMEMORY, hWnd); DsFreeNameResultW(pDsNameResult); return E_OUTOFMEMORY; } DsFreeNameResultW(pDsNameResult); pDsNameResult = NULL; if (fNeedDcBind) { fLoopAgain = TRUE; NameFlags = DS_NAME_NO_FLAGS; fNeedDcBind = FALSE; FormatRequested = OriginalFormatRequested; } } else { pwzNameResult = pDsNameResult->rItems->pName; } break; case DS_NAME_ERROR_RESOLVING: case DS_NAME_ERROR_NOT_FOUND: case DS_NAME_ERROR_NO_MAPPING: // // Can't map the name for some reason, just return the original // name and set the return value to DS_NAME_ERROR_NO_MAPPING. // fLoopAgain = FALSE; pwzNameResult = pwzNameIn; hr = MAKE_HRESULT(SEVERITY_ERROR, 0, DS_NAME_ERROR_NO_MAPPING); break; default: CHECK_WIN32_REPORT(pDsNameResult->rItems->status, hWnd, return MAKE_HRESULT(SEVERITY_ERROR, 0, pDsNameResult->rItems->status)); break; } if (RequestedOpr == GET_NT4_DOMAIN_NAME || RequestedOpr == GET_FQDN_DOMAIN_NAME) { if (CurOpr == GET_FQDN_DOMAIN_NAME) { DO_DEL(*ppwsIn); } if (CurOpr == GET_DNS_DOMAIN_NAME) { if (fGotDnsDomain) { // Got the DNS domain name, now loop once more to finish. // dspAssert(pwzDomain); fLoopAgain = TRUE; CurOpr = RequestedOpr; NameFlags = DS_NAME_NO_FLAGS; if (RequestedOpr == GET_FQDN_DOMAIN_NAME) { // JonN 3/3/99; per DaveStr this should be FormatOffered = DS_CANONICAL_NAME; NameFlags = DS_NAME_FLAG_SYNTACTICAL_ONLY; // was FormatOffered = DS_UNKNOWN_NAME; FormatRequested = DS_FQDN_1779_NAME; pwzDomRoot = new WCHAR[wcslen(pwzDomain) + 2]; CHECK_NULL_REPORT(pwzDomRoot, hWnd, return E_OUTOFMEMORY); wcscpy(pwzDomRoot, pwzDomain); wcscat(pwzDomRoot, L"/"); ppwsIn = &pwzDomRoot; } } else { fLoopAgain = FALSE; } } } } while (fLoopAgain); switch (RequestedOpr) { case GET_OBJ_CAN_NAME: case GET_OBJ_CAN_NAME_EX: case GET_OBJ_1779_DN: case GET_OBJ_NT4_NAME: case GET_FQDN_DOMAIN_NAME: case GET_OBJ_UPN: dspAssert(pDsNameResult); if (FAILED(LocalAllocStringW(ppwzResultName, pwzNameResult))) { DsFreeNameResultW(pDsNameResult); REPORT_ERROR(E_OUTOFMEMORY, hWnd); return E_OUTOFMEMORY; } LocalFreeStringW(&pwzDomain); delete [] pwzDomRoot; pwzDomRoot = NULL; break; case GET_DNS_DOMAIN_NAME: dspAssert(pwzDomain); *ppwzResultName = pwzDomain; break; case GET_NT4_DOMAIN_NAME: dspAssert(pwzNT4Domain); *ppwzResultName = pwzNT4Domain; LocalFreeStringW(&pwzDomain); break; default: dspAssert(FALSE); } if (pDsNameResult) { DsFreeNameResultW(pDsNameResult); } return hr; } #ifdef DSADMIN HRESULT DSPROP_GetGCSearchOnDomain( PWSTR pwzDomainDnsName, IN REFIID iid, OUT void** ppvObject ) { HRESULT hr = S_OK; ASSERT( NULL != ppvObject && NULL == *ppvObject ); CStrW strGC = L"GC:"; if (pwzDomainDnsName) { strGC += L"//"; // strGC += pwzDomainDnsName; PDOMAIN_CONTROLLER_INFO pDCInfo; DWORD dwErr = DsGetDcName (NULL, pwzDomainDnsName, NULL,NULL, DS_DIRECTORY_SERVICE_REQUIRED, &pDCInfo); hr = HRESULT_FROM_WIN32(dwErr); CHECK_HRESULT(hr, return hr); strGC += pDCInfo->DnsForestName; NetApiBufferFree(pDCInfo); } dspDebugOut((DEB_ITRACE, "Binding to %ws\n", strGC)); hr = DSAdminOpenObject(const_cast((LPCWSTR)strGC), iid, (PVOID *)ppvObject); CHECK_HRESULT(hr, return hr); return S_OK; } //+---------------------------------------------------------------------------- // SMTP address validation. This is adapted from code provided by Eric Dao // and Wayne Cranston of the Exchange team. //----------------------------------------------------------------------------- //+---------------------------------------------------------------------------- // // Function: bParse821IPv4Literal // // parse: IPv4literal ::= snum 3("." snum) // snum ::= one, two or three digits representing a decimal // integer in the range 0 through 255 // //----------------------------------------------------------------------------- static BOOL bParse821IPv4Literal(PSTR* ppsz) { PSTR p = *ppsz; DWORD n = 0; DWORD cOctets = 0; DWORD cDigits = 0; dspAssert(*p == '['); while (*p) { p++; n = 0; cDigits = 0; if (!isdigit(*p)) return FALSE; while (isdigit(*p)) { if (++cDigits > 3) return FALSE; n = n * 10 + *p - '0'; p++; } if (++cOctets > 4) return FALSE; if (n > 255) return FALSE; if ('.' != *p) break; } if (']' != *p || cOctets != 4) return FALSE; p++; *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: bParse821AtDomain // // parse: // ::= | "," // ::= "@" // ::= | "." // // IMC does not support # or [127.0.0.1] // ::= | "#" | "[" "]" // ::= // ::= // ::= | // ::= | // ::= | | "-" | "_" ; underscore is invalid, but still used // ::= [A-Za-z] // ::= [0-9] //----------------------------------------------------------------------------- static BOOL bParse821AtDomain(PSTR* ppsz) { PSTR p = *ppsz; dspAssert(*p == '@'); p++; if ('\0' == *p) return FALSE; // Check for IPv4 literals // if ('[' == *p) { if (!bParse821IPv4Literal(&p)) return FALSE; goto Exit; } // // Check to be sure the first character is an alpha // or '#' if (!isalnum(*p) && '#' != *p) { return FALSE; } p++; while (*p) { // NTRAID#NTBUG9-687019-2002/8/13-JeffJon // The second character can be either an alpha-numeric // or a '-' while (*p && (isalnum(*p) || *p == '-')) { p++; } if (!isalnum(*(p-1))) return FALSE; if ('.' != *p) break; p++; } Exit: *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: bParse821String // // parse: // ::= | // ::= | "\" // ::= any of the 128 ascii characters, but not any // or // ::= any one of the 128 ascii characters (no exceptions) // ::= space (ASCII 32) // ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "." // | "," | ";" | ":" | "@" | """ | // ::= ASCII 0 through ASCII 31 inclusive, and ASCII 127 //----------------------------------------------------------------------------- static BOOL bParse821String(PSTR* ppsz) { static char rg821Special[] = "<>()[]\\.,;:@\""; PSTR p = *ppsz; while (*p) { if ('\\' == *p) { p++; if ('\0' == *p) return FALSE; } else { if (' ' == *p || strchr(rg821Special, *p) || *p < ' ' || '\x7f' == *p) return FALSE; } p++; if ('\0' == *p || '@' == *p || '.' == *p) break; // Whitespace encountered at this point could be either trailing (if // there is no @domain), or it is embedded. We can't really tell the // difference (without a lot of work), so fail because there is // no other interpretation that is safe. // if (' ' == *p || '\t' == *p || '>' == *p) return FALSE; } *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: bParse821DotString // // parse: // ::= | "." //----------------------------------------------------------------------------- static BOOL bParse821DotString(PSTR* ppsz) { PSTR p = *ppsz; while (*p) { if (!bParse821String(&p)) return FALSE; if ('.' != *p) break; p++; } *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: bParse821QuotedString // // parse: // ::= """ """ // ::= "\" | "\" | | // ::= any one of the ascii characters except , , // quote (") or backslash (\) //----------------------------------------------------------------------------- static BOOL bParse821QuotedString(PSTR* ppsz) { PSTR p = *ppsz; dspAssert('"' == *p); p++; while (*p && '"' != *p) { if ('\\' == *p) { p++; if ('\0' == *p) return FALSE; } else { if ('\r' == *p || '\n' == *p) return FALSE; } p++; } if ('"' != *p) return FALSE; p++; *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: bParse821Mailbox // // parse: // ::= "@" // ::= | //----------------------------------------------------------------------------- static BOOL bParse821Mailbox(PSTR* ppsz) { PSTR p; BOOL bSuccess = FALSE; dspAssert(ppsz); p = *ppsz; if ('\0' == *p) return FALSE; if ('<' == *p) { p++; if ('"' == *p) bSuccess = bParse821QuotedString(&p); else bSuccess = bParse821DotString(&p); if (!bSuccess) return FALSE; if ('@' == *p && !bParse821AtDomain(&p)) return FALSE; if ('>' != *p) return FALSE; p++; } else { if ('"' == *p) bSuccess = bParse821QuotedString(&p); else bSuccess = bParse821DotString(&p); if (!bSuccess) return FALSE; if ('@' == *p && !bParse821AtDomain(&p)) return FALSE; } *ppsz = p; return TRUE; } //+---------------------------------------------------------------------------- // // Function: FValidSMTPAddress // // Synopsis: Valididate mailbox + domain/host name (RFC 821). // // Return TRUE if we find a valid RFC821 address at pwzBuffer. The UNICODE // buffer is converted to ANSI, checked, then converted back. This insures // that only ANSI characters are in the buffer when done. // //----------------------------------------------------------------------------- BOOL FValidSMTPAddress(PWSTR pwzBuffer) { dspAssert(pwzBuffer); BOOL bUnmappedCharUsed = FALSE; int len; len = WideCharToMultiByte(CP_ACP, 0, pwzBuffer, -1, NULL, 0, NULL, &bUnmappedCharUsed); if (bUnmappedCharUsed) { return FALSE; } PSTR pszBuf = new char[len + 1]; CHECK_NULL_REPORT(pszBuf, GetDesktopWindow(), return FALSE); if (WideCharToMultiByte(CP_ACP, 0, pwzBuffer, -1, pszBuf, len, NULL, NULL) == 0) { ReportError(GetLastError(), 0); delete [] pszBuf; return FALSE; } PSTR p = pszBuf; // working pointer CHECK_NULL_REPORT(p, GetDesktopWindow(), return FALSE); if ('\0' == *p) { delete [] pszBuf; return FALSE; } // Parse routing // if ('@' == *p) { while ('@' == *p) { if (!bParse821AtDomain(&p)) { delete [] pszBuf; return FALSE; } while (' ' == *p || '\t' == *p) { p++; } if (',' != *p) break; p++; while (' ' == *p || '\t' == *p) { p++; } } if (':' != *p) { delete [] pszBuf; return FALSE; } p++; while (' ' == *p || '\t' == *p) { p++; } } if (!bParse821Mailbox(&p)) { delete [] pszBuf; return FALSE; } // If we are not at end of string, then bParse821Mailbox found an invalid // character. // if ('\0' != *p) { delete [] pszBuf; return FALSE; } int lenOrig = len; len = MultiByteToWideChar(CP_ACP, 0, pszBuf, -1, NULL, 0); if (len > lenOrig) { // Assuming the string won't grow when converted to ANSI and back. // dspAssert(len <= lenOrig); delete [] pszBuf; return FALSE; } if (MultiByteToWideChar(CP_ACP, 0, pszBuf, -1, pwzBuffer, len) == 0) { ReportError(GetLastError(), 0); delete [] pszBuf; return FALSE; } delete [] pszBuf; return TRUE; } #endif // DSADMIN void Smart_PADS_ATTR_INFO__Empty( PADS_ATTR_INFO* ppAttrs ) { if (NULL != *ppAttrs) { FreeADsMem( *ppAttrs ); *ppAttrs = NULL; } }