Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2674 lines
77 KiB

//+----------------------------------------------------------------------------
//
// 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 <propcfg.h> // DS Admin definition of PPROPSHEETCFG
#include <ntdsapip.h>
#include <shlobjp.h> // 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<DWORD>(cbStruct);
pDSObj->aObjects[0].offsetClass = static_cast<DWORD>(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<IADsPathname> 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<DWORD>(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<DWORD>(sizeof(DSOBJECT) + nTitleLen*sizeof(WCHAR));
wcscpy((LPWSTR)((BYTE *)pDsObject + pDsObject->offsetName), lpszName);
pDsObject->offsetClass = static_cast<DWORD>(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<IADsPathname> 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<IADsPathname> 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<PWSTR>((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<PWSTR>((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<PWSTR>((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<PWSTR>((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<IADsPathname> 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<PWSTR>((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:
// <a-d-l> ::= <at-domain> | <at-domain> "," <a-d-l>
// <at-domain> ::= "@" <domain>
// <domain> ::= <element> | <element> "." <domain>
// // IMC does not support #<number> or [127.0.0.1]
// <element> ::= <name> | "#" <number> | "[" <dotnum> "]"
// <element> ::= <name>
// <name> ::= <let-dig> <ldh-str> <let-dig>
// <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
// <let-dig> ::= <a> | <d>
// <let-dig-hyp> ::= <a> | <d> | "-" | "_" ; underscore is invalid, but still used
// <a> ::= [A-Za-z]
// <d> ::= [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:
// <string> ::= <char> | <char> <string>
// <char> ::= <c> | "\" <x>
// <c> ::= any of the 128 ascii characters, but not any <special>
// or <sp>
// <x> ::= any one of the 128 ascii characters (no exceptions)
// <sp> ::= space (ASCII 32)
// <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "."
// | "," | ";" | ":" | "@" | """ | <control>
// <control> ::= 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:
// <dot-string> ::= <string> | <string> "." <string>
//-----------------------------------------------------------------------------
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:
// <quoted-string> ::= """ <qtext> """
// <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>
// <q> ::= any one of the ascii characters except <cr>, <lf>,
// 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:
// <mailbox> ::= <local-part> "@" <domain>
// <local-part> ::= <dot-string> | <quoted-string>
//-----------------------------------------------------------------------------
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;
}
}