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
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;
|
|
}
|
|
}
|
|
|