Windows-Server-2003/admin/snapin/dsadmin/dsctx.cpp

3447 lines
107 KiB
C++
Raw Normal View History

2024-08-04 01:28:15 +02:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1999
//
// File: dsctx.cpp
//
// Contents: object to implement context menu extensions
//
// History: 08-dec-97 jimharr Created
//
//
//--------------------------------------------------------------------------
#include "stdafx.h"
#include "resource.h"
#include "util.h"
#include "dsutil.h"
#include "dsctx.h"
#include "dataobj.h"
#include "dscookie.h"
#include "dsdlgs.h"
#include "gsz.h"
#include "querysup.h"
#include "simdata.h"
#include <lm.h>
#include <cmnquery.h> // IPersistQuery
#include <cmnquryp.h> // to get IQueryFrame to notify DS Find
#include <dsquery.h>
#include <dsqueryp.h>
#include <ntlsa.h> // LsaQueryInformationPolicy
const CLSID CLSID_DSContextMenu = { /* 08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6 */
0x08eb4fa6, 0x6ffd, 0x11d1,
{0xb0, 0xe0, 0x00, 0xc0, 0x4f, 0xd8, 0xdc, 0xa6}
};
////////////////////////////////////////////////////////////////////
// Language independent context menu IDs
// WARNING : these should NEVER be changed
// the whole point of having these is so that other
// developers can rely on them being the same no matter
// what language or version they use. The context menus
// can change but their IDs should not
//
#define CMID_ENABLE_ACCOUNT L"_DSADMIN_ENABLE_ACCOUNT"
#define CMID_DISABLE_ACCOUNT L"_DSADMIN_DISABLE_ACCOUNT"
#define CMID_MAP_CERTIFICATES L"_DSADMIN_MAP_CERTIFICATES"
#define CMID_CHANGE_PASSWORD L"_DSADMIN_CHANGE_PASSWORD"
#define CMID_MOVE_OBJECT L"_DSADMIN_MOVE"
#define CMID_DELETE_OBJECT L"_DSADMIN_DELETE"
#define CMID_REPLICATE_NOW L"_DSADMIN_REPLICATE_NOW"
#define CMID_ADD_OBJECTS_TO_GROUP L"_DSADMIN_ADD_TO_GROUP"
#define CMID_COPY_OBJECT L"_DSADMIN_COPY"
#define CMID_RENAME_OBJECT L"_DSADMIN_RENAME"
static CLIPFORMAT g_cfDsObjectNames;
static CLIPFORMAT g_cfDsInternal;
static CLIPFORMAT g_cfCoClass;
static CLIPFORMAT g_cfPropSheetCfg;
static CLIPFORMAT g_cfParentHwnd;
static CLIPFORMAT g_cfComponentData;
///////////////////////////////////////////////////////////////////////////
// CContextMenuSingleDeleteHandler
class CContextMenuSingleDeleteHandler : public CSingleDeleteHandlerBase
{
public:
CContextMenuSingleDeleteHandler(CDSComponentData* pComponentData, HWND hwnd,
LPCWSTR lpszPath, LPCWSTR lpszClass,
BOOL bContainer, CDSContextMenu* pCtxMenu)
: CSingleDeleteHandlerBase(pComponentData, hwnd)
{
m_pCtxMenu = pCtxMenu;
m_lpszPath = lpszPath;
m_lpszClass= lpszClass;
m_bContainer = bContainer;
}
protected:
CDSContextMenu* m_pCtxMenu;
LPCWSTR m_lpszPath;
LPCWSTR m_lpszClass;
BOOL m_bContainer;
virtual HRESULT BeginTransaction()
{
return GetTransaction()->Begin(m_lpszPath, m_lpszClass, m_bContainer, NULL, NULL, FALSE);
}
virtual HRESULT DeleteObject()
{
CString szName;
return m_pCtxMenu->_Delete(m_lpszPath, m_lpszClass, &szName);
}
virtual HRESULT DeleteSubtree()
{
CString szName;
return m_pCtxMenu->_DeleteSubtree(m_lpszPath, &szName);
}
virtual void GetItemName(OUT CString& szName)
{
//
// Clear out any existing value
//
szName.Empty();
CPathCracker pathCracker;
HRESULT hr = pathCracker.Set(CComBSTR(GetItemPath()), ADS_SETTYPE_FULL);
if (SUCCEEDED(hr))
{
hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
if (SUCCEEDED(hr))
{
hr = pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
if (SUCCEEDED(hr))
{
CComBSTR bstrName;
hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &bstrName);
if (SUCCEEDED(hr))
{
szName = bstrName;
}
}
}
}
if (szName.IsEmpty())
{
szName = GetItemPath();
}
}
virtual LPCWSTR GetItemClass(){ return m_lpszClass; }
virtual LPCWSTR GetItemPath(){ return m_lpszPath; }
};
///////////////////////////////////////////////////////////////////////////
// CContextMenuMultipleDeleteHandler
class CContextMenuMultipleDeleteHandler : public CMultipleDeleteHandlerBase
{
public:
CContextMenuMultipleDeleteHandler(CDSComponentData* pComponentData, HWND hwnd,
IDataObject* pDataObject,
CObjectNamesFormatCracker* pObjCracker,
CDSContextMenu* pCtxMenu)
: CMultipleDeleteHandlerBase(pComponentData, hwnd)
{
m_pDataObject = pDataObject;
m_pObjCracker = pObjCracker;
m_pCtxMenu = pCtxMenu;
ASSERT(m_pObjCracker->GetCount() > 1);
// allocate an array of BOOL's to keep track of what actually got deleted
// and initialize ot to zero (FALSE)
m_pDeletedArr = new BOOL[GetItemCount()];
::ZeroMemory(m_pDeletedArr, sizeof(BOOL)*GetItemCount());
}
virtual ~CContextMenuMultipleDeleteHandler()
{
delete[] m_pDeletedArr;
}
BOOL WasItemDeleted(UINT i)
{
ASSERT(i < GetItemCount());
return m_pDeletedArr[i];
}
protected:
virtual UINT GetItemCount() { return m_pObjCracker->GetCount();}
virtual HRESULT BeginTransaction()
{
return GetTransaction()->Begin(m_pDataObject, NULL, NULL, FALSE);
}
virtual HRESULT DeleteObject(UINT i)
{
bool fAlternateDeleteMethod = false;
LPCWSTR lpszObjectPath = m_pObjCracker->GetName(i);
LPCWSTR lpszObjectClass = m_pObjCracker->GetClass(i);
HRESULT hr = ObjectDeletionCheck(
lpszObjectPath,
NULL,
lpszObjectClass,
fAlternateDeleteMethod );
if (FAILED(hr) || HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
return hr;
if (!fAlternateDeleteMethod)
{
CString szName;
hr = m_pCtxMenu->_Delete(lpszObjectPath, lpszObjectClass, &szName);
}
if (SUCCEEDED(hr) && (hr != S_FALSE))
{
m_pDeletedArr[i] = TRUE;
}
return hr;
}
virtual HRESULT DeleteSubtree(UINT i)
{
CString szName;
HRESULT hr = m_pCtxMenu->_DeleteSubtree(m_pObjCracker->GetName(i), &szName);
if (SUCCEEDED(hr) && (hr != S_FALSE))
{
m_pDeletedArr[i] = TRUE;
}
return hr;
}
virtual void GetItemName(IN UINT i, OUT CString& szName)
{
//
// Clear out any existing value
//
szName.Empty();
CPathCracker pathCracker;
HRESULT hr = pathCracker.Set(CComBSTR(m_pObjCracker->GetName(i)), ADS_SETTYPE_FULL);
if (SUCCEEDED(hr))
{
hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
if (SUCCEEDED(hr))
{
hr = pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
if (SUCCEEDED(hr))
{
CComBSTR bstrName;
hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &bstrName);
if (SUCCEEDED(hr))
{
szName = bstrName;
}
}
}
}
if (szName.IsEmpty())
{
szName = m_pObjCracker->GetName(i);
}
}
virtual void GetItemPath(UINT i, CString& szPath)
{
szPath = m_pObjCracker->GetName(i);
}
virtual PCWSTR GetItemClass(UINT i)
{
return m_pObjCracker->GetClass(i);
}
private:
IDataObject* m_pDataObject;
CObjectNamesFormatCracker* m_pObjCracker;
CDSContextMenu* m_pCtxMenu;
BOOL* m_pDeletedArr;
};
///////////////////////////////////////////////////////////////////////////
// ContextMenu
class CContextMenuMoveHandler : public CMoveHandlerBase
{
public:
CContextMenuMoveHandler(CDSComponentData* pComponentData, HWND hwnd,
LPCWSTR lpszBrowseRootPath,
IDataObject* pDataObject,
CInternalFormatCracker* pInternalFormatCracker,
CObjectNamesFormatCracker* pObjectNamesFormatCracker)
: CMoveHandlerBase(pComponentData, hwnd, lpszBrowseRootPath)
{
m_pDataObject = pDataObject;
m_pInternalFormatCracker = pInternalFormatCracker;
m_pObjectNamesFormatCracker = pObjectNamesFormatCracker;
}
protected:
virtual UINT GetItemCount() { return m_pObjectNamesFormatCracker->GetCount();}
virtual HRESULT BeginTransaction()
{
return GetTransaction()->Begin(m_pDataObject,
GetDestPath(), GetDestClass(), IsDestContainer());
}
virtual void GetNewPath(UINT i, CString& szNewPath)
{
szNewPath = m_pObjectNamesFormatCracker->GetName(i);
}
virtual void GetItemPath(UINT i, CString& szPath)
{
szPath = m_pObjectNamesFormatCracker->GetName(i);
}
virtual PCWSTR GetItemClass(UINT i)
{
return m_pObjectNamesFormatCracker->GetClass(i);
}
virtual void GetName(UINT i, CString& strref)
{
HRESULT hr = S_OK;
if (m_pInternalFormatCracker->HasData())
{
CUINode* pUINode = m_pInternalFormatCracker->GetCookie(i);
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
if (pCookie != NULL)
{
strref = pCookie->GetName();
}
else
{
strref = L"";
}
return;
}
else
{
// REVIEW_MARCOC_PORT: this might be inefficent, need to make a member variable
CPathCracker pathCracker;
hr = pathCracker.Set(CComBSTR(m_pObjectNamesFormatCracker->GetName(i)),
ADS_SETTYPE_FULL);
hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
CComBSTR DestName;
hr = pathCracker.GetElement( 0, &DestName );
strref = DestName;
return;
}
}
virtual HRESULT OnItemMoved(UINT i, IADs* /*pIADs*/)
{
HRESULT hr = S_OK;
if (m_pInternalFormatCracker != NULL && m_pInternalFormatCracker->HasData())
{
CUINode* pUINode = m_pInternalFormatCracker->GetCookie(i);
pUINode->SetExtOp(OPCODE_MOVE);
/* REVIEW_JEFFJON : removed due to bug 190532 After changing view from list to detail
and back to list the drag and drop does not work from query window
We decided that saved queries will be snapshots of the time
they are run and we should not try to keep them updated.
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
if (pCookie != NULL)
{
CUINode* pParentNode = pUINode->GetParent();
if (pParentNode != NULL && !IS_CLASS(pParentNode, DS_UI_NODE))
{
CComBSTR bsPath;
hr = pIADs->get_ADsPath(&bsPath);
if (SUCCEEDED(hr))
{
CString szPath;
StripADsIPath(bsPath, szPath);
pCookie->SetPath(szPath);
}
}
}
*/
}
return hr;
}
virtual void GetClassOfMovedItem(CString& szClass)
{
szClass.Empty();
if (NULL == m_pObjectNamesFormatCracker)
return;
UINT nCount = GetItemCount();
if (0 == nCount)
return;
szClass = m_pObjectNamesFormatCracker->GetClass(0);
for (UINT i = 1; i < nCount; i++)
{
if (0 != szClass.CompareNoCase( m_pObjectNamesFormatCracker->GetClass(i) ))
{
szClass.Empty();
return;
}
}
}
private:
IDataObject* m_pDataObject;
CInternalFormatCracker* m_pInternalFormatCracker;
CObjectNamesFormatCracker* m_pObjectNamesFormatCracker;
};
///////////////////////////////////////////////////////////////////////////
// CDSContextMenu
CDSContextMenu::CDSContextMenu()
{
m_pDsObject = NULL;
m_fClasses = 0;
m_hwnd = NULL;
m_pCD = NULL;
}
CDSContextMenu::~CDSContextMenu()
{
if (m_pDsObject) {
m_pDsObject->Release();
}
}
const UINT Type_User = 0x0001;
const UINT Type_Group = 0x0002;
const UINT Type_Computer = 0x0004;
const UINT Type_NTDSConnection = 0x0008;
const UINT Type_TrueNTDSConnection = 0x0010;
const UINT Type_FRSConnection = 0x0020;
const UINT Type_Domain = 0x0040;
const UINT Type_Contact = 0x0080;
const UINT Type_OU = 0x0100;
const UINT Type_Others = 0x8000;
extern CDSComponentData* g_pCD;
////////////////////////////////////////////////////////////
// IShellExtInit methods
STDMETHODIMP
CDSContextMenu::Initialize(LPCITEMIDLIST,
LPDATAOBJECT pDataObj,
HKEY)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HRESULT hr = 0;
USES_CONVERSION;
TRACE(_T("CDsContextMenu::Initialize.\n"));
TIMER(_T("Entering DSContext Init().\n"));
if (pDataObj == NULL)
{
return E_INVALIDARG; // no point in going on
}
// hold on to the data object
m_spDataObject = pDataObj;
// get path and class info: this format is always needed
hr = m_objectNamesFormat.Extract(pDataObj);
if (FAILED(hr))
return hr;
// we need at least one object in the selection
ASSERT(m_objectNamesFormat.HasData());
if (m_objectNamesFormat.GetCount() == 0)
{
TRACE (_T("DSContextMenu::Init: can't find path\n"));
return S_OK;
}
// get DSADMIN internal format (it can be there or not)
// if present, we are called from DS Admin
m_internalFormat.Extract(pDataObj);
// get extra info
_GetExtraInfo(pDataObj);
// check whether an NTDSConnection is actually an FRS connection
if (m_fClasses & Type_NTDSConnection)
{
//
// check whether this is an NTDS instance or an FRS instance
// CODEWORK this will not work outside of DSADMIN (e.g. in DSFIND)
//
if ( m_internalFormat.HasData()
&& NULL != m_internalFormat.GetCookie(0)
&& NULL != m_internalFormat.GetCookie(0)->GetParent() )
{
CUINode* pUIParentNode = m_internalFormat.GetCookie(0)->GetParent();
CDSCookie* pParentCookie = GetDSCookieFromUINode(pUIParentNode);
CString strClass = pParentCookie->GetClass();
bool fParentIsFrs = false;
HRESULT hr2 = DSPROP_IsFrsObject(
const_cast<LPWSTR>((LPCTSTR)strClass), &fParentIsFrs );
ASSERT( SUCCEEDED(hr2) );
if ( SUCCEEDED(hr2) )
m_fClasses |= ( (fParentIsFrs) ? Type_FRSConnection : Type_TrueNTDSConnection );
}
}
TIMER(_T("Exiting DSContext Init().\n"));
return hr;
}
///////////////////////////////////////////////////////////
// IContextMenu methods
STDMETHODIMP
CDSContextMenu::QueryContextMenu(HMENU hMenu,
UINT indexMenu,
UINT idCmdFirst,
UINT,
UINT)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HRESULT hr = S_OK;
TCHAR szBuffer[MAX_PATH];
const INT cItems = 10; //max num of items added
UINT nLargestCmd = 0;
CComVariant CurrentState;
BOOL bEnableMove = TRUE;
BOOL bEnableDelete = TRUE;
BOOL bEnableRename = TRUE;
TRACE(_T("CDsContextMenu::QueryContextMenu.\n"));
TIMER(_T("Entering DSContext QCM().\n"));
if (m_internalFormat.HasData())
{
int iSystemFlags = 0;
DWORD i = 0;
//
// Loop through all the selected nodes adding the appropriate menu items
//
for (i=0; i < m_internalFormat.GetCookieCount(); i++)
{
CUINode* pUINode = m_internalFormat.GetCookie(i);
iSystemFlags = GetDSCookieFromUINode(pUINode)->GetSystemFlags();
switch (m_internalFormat.GetSnapinType()) // assume multi-selected items all have the same snapin type
{
case SNAPINTYPE_DS:
case SNAPINTYPE_DSEX:
bEnableMove = bEnableMove &&
!(iSystemFlags & FLAG_DOMAIN_DISALLOW_MOVE);
bEnableDelete = bEnableDelete &&
!(iSystemFlags & FLAG_DISALLOW_DELETE);
bEnableRename = bEnableRename &&
!(iSystemFlags & FLAG_DOMAIN_DISALLOW_RENAME);
break;
case SNAPINTYPE_SITE:
bEnableMove = bEnableMove &&
( iSystemFlags & (FLAG_CONFIG_ALLOW_MOVE | FLAG_CONFIG_ALLOW_LIMITED_MOVE) );
bEnableDelete = bEnableDelete &&
!(iSystemFlags & FLAG_DISALLOW_DELETE);
bEnableRename = bEnableRename &&
(iSystemFlags & FLAG_CONFIG_ALLOW_RENAME);
break;
default:
break;
} // switch
} // end of for loop
} // if
//
// add them items to your menu, inserting them at indexMenu + the offset for your
// item. idCmdFirst / idCmdList is the range you should use, they should
// not exceed this range. On exit return the number of items and IDs you claimed,
//
//
// Add the Move menu item if this is the Sites snapin
// CODEWORK JonN 8/21/01 "if this is NOT the Sites snapin"
//
if ((m_CallerSnapin != CLSID_SiteSnapin) &&
!(m_fClasses & Type_Domain) &&
bEnableMove &&
(m_pCD != NULL))
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_MOVE_OBJECT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_MOVE_OBJECT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_MOVE_OBJECT);
}
//
// If this is a User or Computer object add the Reset Account menu item
// It is done this way so that if m_fClasses contains more than computer
// and object types in conjuction with user and/or object types we fail
//
if ( m_fClasses && !(m_fClasses & ~(Type_User | Type_Computer)))
{
if (m_objectNamesFormat.GetCount() == 1)
{
//
// Load the string for the menu item
//
if (m_fClasses == Type_Computer) // Computer
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_RESET_ACCOUNT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
}
else // User
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_CHANGE_PASSWORD,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
}
//
// Insert the menu item
//
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_CHANGE_PASSWORD,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_CHANGE_PASSWORD);
//
// Bind and figure out if the account is disabled
// then add a menu item to enable or disable the account accordingly
//
hr = DSAdminOpenObject(m_objectNamesFormat.GetName(0),
IID_IADsUser,
(void **)&m_pDsObject,
TRUE /*bServer*/);
if (SUCCEEDED(hr))
{
hr = m_pDsObject->Get(CComBSTR(L"userAccountControl"), &CurrentState);
if (SUCCEEDED(hr))
{
m_UserAccountState = CurrentState.lVal;
if (!(m_UserAccountState & UF_SERVER_TRUST_ACCOUNT))
{
if ((m_UserAccountState & UF_ACCOUNTDISABLE))
{
//
// Account is disabled... Load the enable string and insert
// the menu item
//
if ( !LoadString(AfxGetInstanceHandle(), IDS_ENABLE_ACCOUNT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_ENABLE_ACCOUNT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_ENABLE_ACCOUNT);
}
else
{
//
// Account is enabled... Load the disable string and insert
// the menu item.
//
if ( !LoadString(AfxGetInstanceHandle(), IDS_DISABLE_ACCOUNT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_DISABLE_ACCOUNT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_DISABLE_ACCOUNT);
}
}
} // if get userAccountControl succeeded
} // if bind succeeded
if (m_Advanced)
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_MAP_CERTIFICATES,
szBuffer, ARRAYLEN(szBuffer)) ) {
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_MAP_CERTIFICATES,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_MAP_CERTIFICATES);
}
}
else // m_objectNamesFormat.GetCount() != 1
{
if (m_fClasses && !(m_fClasses & ~(Type_User | Type_Computer)))
{
//
// Load the enable account menu item
//
if ( !LoadString(AfxGetInstanceHandle(), IDS_ENABLE_ACCOUNT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_ENABLE_ACCOUNT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_ENABLE_ACCOUNT);
//
// Load the disable account menu item
//
if ( !LoadString(AfxGetInstanceHandle(), IDS_DISABLE_ACCOUNT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_DISABLE_ACCOUNT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_DISABLE_ACCOUNT);
} // if (m_objectNamesFormat.GetCount() == 1)
}
} // if User or Computer
//
// If the node is a user, or contact insert Add objects to group menu item
// Note: OU removed 08/02/2000 by JeffJon
//
if (m_fClasses && !(m_fClasses & ~(Type_User | Type_Contact)))
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_ADD_TO_GROUP,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
BOOL bInsertSuccess = InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_ADD_OBJECTS_TO_GROUP,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_ADD_OBJECTS_TO_GROUP);
if (!bInsertSuccess)
{
TRACE(_T("Failed to insert Add to group context menu item. 0x%x\n"), GetLastError());
}
}
//
// If we weren't called from MMC
//
if (!m_internalFormat.HasData())
{
//
// Insert the delete menu item if appropriate
//
if ( !LoadString(AfxGetInstanceHandle(), IDS_DELETE,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
if (bEnableDelete)
{
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_DELETE_OBJECT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_DELETE_OBJECT);
}
//
// If single selection and node is a computer insert the rename menu item
// NOTE : the rename handler is heavily dependent on DSAdmin being the caller
// hence the check for m_pCD.
//
if (m_pCD != NULL &&
(m_objectNamesFormat.GetCount() == 1) &&
!(m_fClasses & Type_Computer))
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_RENAME,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
if (bEnableRename)
{
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_RENAME_OBJECT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_RENAME_OBJECT);
}
}
} // if not called from mmc
//
// If node type is NTDSConnection insert the replicate now menu item
//
if (m_fClasses & Type_TrueNTDSConnection)
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_REPLICATE_NOW,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_REPLICATE_NOW,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_REPLICATE_NOW);
} // node type NTDSConnection
//
// If node type is user and we can copy it the add the copy object menu item
//
if ( (m_pCD != NULL) && (m_objectNamesFormat.GetCount() == 1) && (m_fClasses == Type_User) )
{
if (S_OK == m_pCD->_CanCopyDSObject(m_spDataObject))
{
if ( !LoadString(AfxGetInstanceHandle(), IDS_COPY_OBJECT,
szBuffer, ARRAYLEN(szBuffer)) )
{
TRACE(_T("Failed to load resource for menu item. hr is %lx\n"), hr);
goto exit_gracefully;
}
InsertMenu(hMenu,
indexMenu,
MF_BYPOSITION|MF_STRING,
idCmdFirst+IDC_COPY_OBJECT,
szBuffer);
nLargestCmd = __max(nLargestCmd, idCmdFirst+IDC_COPY_OBJECT);
} // if
} // if
hr = S_OK;
exit_gracefully:
if (SUCCEEDED(hr))
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, cItems);
TIMER(_T("Exiting DSContext QCM().\n"));
return hr;
}
STDMETHODIMP
CDSContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HRESULT hr = S_OK;
TRACE (_T("CDSContextMenu::InvokeCommand\n"));
if (lpcmi->hwnd != m_hwnd)
{
m_hwnd = lpcmi->hwnd;
}
TRACE (_T("\tlpcmi->lpVerb is %d.\n"), lpcmi->lpVerb);
switch ((INT_PTR)(lpcmi->lpVerb)) {
case IDC_ENABLE_ACCOUNT:
if (m_objectNamesFormat.GetCount() == 1) {
if (m_UserAccountState & UF_ACCOUNTDISABLE) {
DisableAccount(FALSE);
} else {
DisableAccount(TRUE);
}
} else {
DisableAccount(FALSE);
}
break;
case IDC_DISABLE_ACCOUNT:
DisableAccount(TRUE);
break;
case IDC_MAP_CERTIFICATES:
{
ASSERT (m_objectNamesFormat.GetCount() == 1);
LPWSTR pszCanonical = NULL;
CString szName = m_objectNamesFormat.GetName(0);
CString szPath;
StripADsIPath(szName, szPath, false); // don't use escaped mode
// we don't care about the return code here
CrackName((LPWSTR) (LPCWSTR)szPath,
&pszCanonical,
GET_OBJ_CAN_NAME,
NULL);
CSimData simData;
if ( simData.FInit(pszCanonical, szName, m_hwnd))
{
CThemeContextActivator activator;
simData.DoModal();
}
else
hr = E_FAIL;
if ( pszCanonical )
LocalFreeStringW(&pszCanonical);
return hr;
}
break;
case IDC_CHANGE_PASSWORD:
ASSERT (m_objectNamesFormat.GetCount() == 1);
ModifyPassword();
break;
case IDC_MOVE_OBJECT:
MoveObject();
break;
case IDC_DELETE_OBJECT:
TRACE(_T("called Delete in context menu extension\n"));
DeleteObject();
break;
case IDC_REPLICATE_NOW:
ReplicateNow();
break;
case IDC_ADD_OBJECTS_TO_GROUP:
AddToGroup();
break;
case IDC_COPY_OBJECT:
CopyObject();
break;
case IDC_RENAME_OBJECT:
Rename();
break;
}
return hr;
}
STDMETHODIMP
CDSContextMenu::GetCommandString(UINT_PTR idCmd,
UINT uFlags,
UINT FAR*,
LPSTR pszName,
UINT ccMax)
{
HRESULT hr = S_OK;
TRACE (_T("CDSContextMenu::GetCommandString\n"));
TRACE (_T("\tidCmd is %d.\n"), idCmd);
if (uFlags == GCS_HELPTEXT)
{
CString csHelp;
switch ((idCmd))
{
case IDC_ENABLE_ACCOUNT:
csHelp.LoadString (IDS_ENABLE_ACCOUNT_HELPSTRING);
break;
case IDC_DISABLE_ACCOUNT:
csHelp.LoadString (IDS_DISABLE_ACCOUNT_HELPSTRING);
break;
case IDC_MAP_CERTIFICATES:
csHelp.LoadString (IDS_MAP_CERTS_HELPSTRING);
break;
case IDC_CHANGE_PASSWORD:
csHelp.LoadString (IDS_CHANGE_PWD_HELPSTRING);
break;
case IDC_MOVE_OBJECT:
csHelp.LoadString (IDS_MOVE_OBJECT_HELPSTRING);
break;
case IDC_DELETE_OBJECT:
csHelp.LoadString (IDS_DELETE_OBJECT_HELPSTRING);
break;
case IDC_REPLICATE_NOW:
csHelp.LoadString (IDS_REPLICATE_HELPSTRING);
break;
case IDC_ADD_OBJECTS_TO_GROUP:
csHelp.LoadString (IDS_ADD_OBJECTS_HELPSTRING);
break;
case IDC_COPY_OBJECT:
csHelp.LoadString (IDS_COPY_HELPSTRING);
break;
case IDC_RENAME_OBJECT:
csHelp.LoadString (IDS_RENAME_HELPSTRING);
break;
}
//NTRAID#NTBUG9-567482-2002/03/10-jmessec There is no release-mode code to validate length < ccMax; could
//cause a buffer overflow of pszName; especially since buffer length is defined by caller
ASSERT ((UINT)csHelp.GetLength() < ccMax);
wcscpy ((LPWSTR)pszName, (LPWSTR)(LPCWSTR)csHelp);
}
else if (uFlags == GCS_VERB)
{
//
// Return the language independent ID of the context menu item
//
CString szMenuID;
switch ((idCmd))
{
case IDC_ENABLE_ACCOUNT:
szMenuID = CMID_ENABLE_ACCOUNT;
break;
case IDC_DISABLE_ACCOUNT:
szMenuID = CMID_DISABLE_ACCOUNT;
break;
case IDC_MAP_CERTIFICATES:
szMenuID = CMID_MAP_CERTIFICATES;
break;
case IDC_CHANGE_PASSWORD:
szMenuID = CMID_CHANGE_PASSWORD;
break;
case IDC_MOVE_OBJECT:
szMenuID = CMID_MOVE_OBJECT;
break;
case IDC_DELETE_OBJECT:
szMenuID = CMID_DELETE_OBJECT;
break;
case IDC_REPLICATE_NOW:
szMenuID = CMID_REPLICATE_NOW;
break;
case IDC_ADD_OBJECTS_TO_GROUP:
szMenuID = CMID_ADD_OBJECTS_TO_GROUP;
break;
case IDC_COPY_OBJECT:
szMenuID = CMID_COPY_OBJECT;
break;
case IDC_RENAME_OBJECT:
szMenuID = CMID_RENAME_OBJECT;
break;
}
//NTRAID#NTBUG9-567482-2002/03/10-jmessec There is no release-mode code to validate length < ccMax; could
//cause a buffer overflow of pszName; especially since buffer length is defined by caller
ASSERT ((UINT)szMenuID.GetLength() < ccMax);
wcscpy ((LPWSTR)pszName, (LPWSTR)(LPCWSTR)szMenuID);
}
else
{
TRACE(_T("We are not supporting any other GetCommandString() flags besides GCS_VERB and GCS_HELPTEXT"));
return E_INVALIDARG;
}
return hr;
}
void CDSContextMenu::_ToggleDisabledIcon(UINT index, BOOL bDisable)
{
if ( (m_pCD != NULL) && m_internalFormat.HasData())
{
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(m_internalFormat.GetCookie(index));
ASSERT(pDSUINode != NULL);
if (pDSUINode == NULL)
return;
m_pCD->ToggleDisabled(pDSUINode, bDisable);
}
}
void CDSContextMenu::DisableAccount(BOOL bDisable)
{
HRESULT hr = S_OK;
HRESULT hr2 = S_OK;
CComVariant Disabled;
DWORD Response = IDYES;
if (m_objectNamesFormat.GetCount() == 1) { // single selection
if (m_pDsObject) {
if (((bDisable) && (!(m_UserAccountState & UF_ACCOUNTDISABLE))) ||
((!bDisable) && (m_UserAccountState & UF_ACCOUNTDISABLE))) {
Disabled.vt = VT_I4;
Disabled.lVal = m_UserAccountState;
if (bDisable == TRUE) {
Disabled.lVal |= UF_ACCOUNTDISABLE;
} else {
Disabled.lVal &= ~UF_ACCOUNTDISABLE;
}
// prep for display by getting obj name
CPathCracker pathCracker;
hr2 = pathCracker.Set(CComBSTR(m_objectNamesFormat.GetName(0)), ADS_SETTYPE_FULL);
ASSERT(SUCCEEDED(hr2));
hr2 = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
ASSERT(SUCCEEDED(hr2));
CComBSTR DestName;
LONG iEMode = 0;
hr2 = pathCracker.get_EscapedMode(&iEMode);
ASSERT(SUCCEEDED(hr2));
hr2 = pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
ASSERT(SUCCEEDED(hr2));
hr2 = pathCracker.GetElement( 0, &DestName );
ASSERT(SUCCEEDED(hr2));
hr2 = pathCracker.put_EscapedMode(iEMode);
ASSERT(SUCCEEDED(hr2));
PVOID apv[1] = {(LPWSTR)DestName};
CString strClass = m_objectNamesFormat.GetClass(0);
if ((strClass == "computer") && (bDisable)) {
Response = ReportErrorEx (m_hwnd,IDS_12_DISABLE_COMPUTER_P,hr,
MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING, apv, 1);
}
if (Response == IDYES) {
hr = m_pDsObject->Put(CComBSTR(L"userAccountControl"), Disabled);
hr = m_pDsObject->SetInfo();
if (SUCCEEDED(hr)) {
_ToggleDisabledIcon(0, bDisable);
if (bDisable) {
ReportErrorEx (m_hwnd,IDS_12_USER_DISABLED_SUCCESSFULLY,hr,
MB_OK | MB_ICONINFORMATION, apv, 1);
}
else {
ReportErrorEx (m_hwnd,IDS_12_USER_ENABLED_SUCCESSFULLY,hr,
MB_OK | MB_ICONINFORMATION, apv, 1);
}
} else {
if (bDisable) {
ReportErrorEx (m_hwnd,IDS_12_USER_DISABLE_FAILED,hr,
MB_OK | MB_ICONERROR, apv, 1);
} else
ReportErrorEx (m_hwnd,IDS_12_USER_ENABLE_FAILED,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
}
}
} else {
PVOID apv[1] = {(LPWSTR)m_objectNamesFormat.GetName(0)};
ReportErrorEx (m_hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
}
else //multiple selection
{
UINT index;
IADsUser * pObj = NULL;
CComVariant CurrentState;
DWORD UserAccountState;
BOOL error = FALSE;
DWORD ResponseToo = IDYES;
if ((m_fClasses & Type_Computer) && (bDisable))
{
ResponseToo = ReportMessageEx (m_hwnd, IDS_MULTI_DISABLE_COMPUTER,
MB_YESNO | MB_ICONWARNING);
}
if (ResponseToo == IDYES)
{
for (index = 0; index < m_objectNamesFormat.GetCount(); index++)
{
hr = DSAdminOpenObject(m_objectNamesFormat.GetName(index),
IID_IADsUser,
(void **)&pObj,
TRUE /*bServer*/);
if (SUCCEEDED(hr))
{
hr = pObj->Get(CComBSTR(L"userAccountControl"), &CurrentState);
if (SUCCEEDED(hr))
{
UserAccountState = CurrentState.lVal;
if (((bDisable) && (!(UserAccountState & UF_ACCOUNTDISABLE))) ||
((!bDisable) && (UserAccountState & UF_ACCOUNTDISABLE)))
{
Disabled.vt = VT_I4;
Disabled.lVal = UserAccountState;
if (bDisable == TRUE)
{
Disabled.lVal |= UF_ACCOUNTDISABLE;
}
else
{
Disabled.lVal &= ~UF_ACCOUNTDISABLE;
}
hr = pObj->Put(CComBSTR(L"userAccountControl"), Disabled);
hr = pObj->SetInfo();
if (FAILED(hr))
{
error = TRUE;
break;
}
else
{
_ToggleDisabledIcon(index, bDisable);
}
}
}
pObj->Release();
}
else
{
// prep for display by getting obj name
CPathCracker pathCracker;
hr2 = pathCracker.Set(CComBSTR(m_objectNamesFormat.GetName(index)), ADS_SETTYPE_FULL);
ASSERT(SUCCEEDED(hr2));
hr2 = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
ASSERT(SUCCEEDED(hr2));
CComBSTR ObjName;
hr2 = pathCracker.GetElement( 0, &ObjName );
ASSERT(SUCCEEDED(hr2));
PVOID apv[1] = {(LPWSTR)ObjName};
ReportErrorEx (m_hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
}
}
if (error)
{
if (bDisable)
ReportErrorEx (m_hwnd,IDS_DISABLES_FAILED,hr,
MB_OK | MB_ICONERROR, NULL, 0);
else
ReportErrorEx (m_hwnd,IDS_ENABLES_FAILED,hr,
MB_OK | MB_ICONERROR, NULL, 0);
}
else
{
if (bDisable)
{
ReportErrorEx (m_hwnd, IDS_DISABLED_SUCCESSFULLY, S_OK,
MB_OK | MB_ICONINFORMATION, NULL, 0);
}
else
{
ReportErrorEx (m_hwnd, IDS_ENABLED_SUCCESSFULLY, S_OK,
MB_OK | MB_ICONINFORMATION, NULL, 0);
}
}
}
}
void CDSContextMenu::ModifyPassword()
{
HRESULT hr = S_OK;
CString NewPwd, CfmPwd;
CComVariant Var;
BOOL error;
LPCWSTR lpszClass, lpszPath;
CChangePassword ChgDlg;
CWaitCursor CWait;
lpszPath = m_objectNamesFormat.GetName(0);
// prep for display by getting obj name
CPathCracker pathCracker;
hr = pathCracker.Set(CComBSTR(lpszPath), ADS_SETTYPE_FULL);
hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
hr = pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
CComBSTR ObjName;
hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &ObjName );
PVOID apv[1] = {(LPWSTR)ObjName};
if (!m_pDsObject) {
hr = DSAdminOpenObject(lpszPath,
IID_IADsUser,
(void **)&m_pDsObject,
TRUE /*bServer*/);
if (FAILED(hr)) {
ReportErrorEx (m_hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
MB_OK | MB_ICONERROR, apv, 1);
goto exit_gracefully;
}
}
lpszClass = m_objectNamesFormat.GetClass(0);
//
// Get the userAccountControl
//
ASSERT(SUCCEEDED(hr));
hr = m_pDsObject->Get(CComBSTR(L"userAccountControl"), &Var);
if (wcscmp(lpszClass, L"computer") == 0)
{
if (FAILED(hr) || ((Var.lVal & UF_SERVER_TRUST_ACCOUNT) != 0))
{
ReportErrorEx (m_hwnd, IDS_1_CANT_RESET_DOMAIN_CONTROLLER, S_OK,
MB_OK | MB_ICONERROR, apv, 1);
}
else
{
DWORD Response = IDYES;
Response = ReportMessageEx (m_hwnd, IDS_CONFIRM_PASSWORD,
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
if (Response == IDYES)
{
hr = m_pDsObject->Get(CComBSTR(L"sAMAccountName"), &Var);
ASSERT(SUCCEEDED(hr));
NewPwd = Var.bstrVal;
NewPwd = NewPwd.Left(14);
INT loc = NewPwd.Find(L"$");
if (loc > 0)
{
NewPwd = NewPwd.Left(loc);
}
// NTRAID#NTBUG9-483038-10/18/2001-JeffJon
// CString::MakeLower() doesn't lower case German characters
// correctly. Using _wcslwr instead. setlocale must be
// called before calling _wcslwr so that it properly lowercases
// extended characters. Store the result and call setlocale
// again when done to set it back to the original so as not
// to affect other snapins in the process
PWSTR oldLocale = _wsetlocale(LC_ALL, L"");
CString lowerCaseNewPwd = _wcslwr((LPWSTR)(LPCWSTR)NewPwd);
_wsetlocale(LC_ALL, oldLocale);
if (SUCCEEDED(hr))
{
hr = m_pDsObject->SetPassword(CComBSTR(lowerCaseNewPwd));
if (SUCCEEDED(hr))
{
ReportErrorEx (m_hwnd,IDS_1_RESET_ACCOUNT_SUCCESSFULL,hr,
MB_OK | MB_ICONINFORMATION, apv, 1);
}
else
{
ReportErrorEx (m_hwnd,IDS_12_RESET_ACCOUNT_FAILED,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
}
}
}
}
else // Not computer object
{
//
// If password doesn't expire don't allow the checkbox for
// requiring the user to change the password at next logon
//
if (Var.lVal & UF_DONT_EXPIRE_PASSWD)
{
ChgDlg.AllowMustChangePasswordCheck(FALSE);
}
//
// NTRAID#Windows Bugs-278296-2001/01/12-jeffjon
// Checking "user must change password" on the Reset Pwd dialog
// is silently ignored when Write PwdLastSet not granted
//
// Disable the checkbox if the admin doesn't have the right
// to write the pwdLastSet attribute
//
BOOL bAllowMustChangePassword = FALSE;
CComPtr<IDirectoryObject> spDirObject;
hr = m_pDsObject->QueryInterface(IID_IDirectoryObject, (void**)&spDirObject);
if (SUCCEEDED(hr))
{
PWSTR ppAttrs[] = { (PWSTR)g_pszAllowedAttributesEffective };
DWORD dwAttrsReturned = 0;
PADS_ATTR_INFO pAttrInfo = 0;
hr = spDirObject->GetObjectAttributes(ppAttrs, 1, &pAttrInfo, &dwAttrsReturned);
if (SUCCEEDED(hr) && dwAttrsReturned == 1 && pAttrInfo)
{
if (pAttrInfo->pszAttrName &&
0 == _wcsicmp(pAttrInfo->pszAttrName, g_pszAllowedAttributesEffective) &&
pAttrInfo->pADsValues)
{
for (DWORD dwIdx = 0; dwIdx < pAttrInfo->dwNumValues; ++dwIdx)
{
if (pAttrInfo->pADsValues[dwIdx].CaseIgnoreString &&
_wcsicmp(pAttrInfo->pADsValues[dwIdx].CaseIgnoreString, g_pszPwdLastSet))
{
bAllowMustChangePassword = TRUE;
break;
}
}
}
}
//
// Disable the checkbox if the user object doesn't have rights
// to change their password
//
if (!CanUserChangePassword(spDirObject))
{
bAllowMustChangePassword = FALSE;
}
}
if (!bAllowMustChangePassword)
{
ChgDlg.AllowMustChangePasswordCheck(FALSE);
}
do
{
error = FALSE;
CThemeContextActivator activator;
if (ChgDlg.DoModal() == IDOK)
{
// NTRAID#NTBUG9-635046-2002/06/10-artm
hr = ChgDlg.GetLastEncryptionResult();
if (FAILED(hr))
{
// For whatever reason, we failed to correctly store
// the password(s) the user typed. Tell the user that
// the operation failed, and retry getting password.
ReportErrorEx(
m_hwnd,
IDS_12_PASSWORD_CHANGE_FAILED,
hr,
MB_OK | MB_ICONERROR,
apv,
1);
error = TRUE;
continue;
}
CWaitCursor CWait2;
const EncryptedString newPwd = ChgDlg.GetNew();
const EncryptedString cfmPwd = ChgDlg.GetConfirm();
if (newPwd == cfmPwd)
{
if (SUCCEEDED(hr))
{
WCHAR* clearText = newPwd.GetClearTextCopy();
if (NULL == clearText)
{
// Ran out of memory!
hr = E_OUTOFMEMORY;
newPwd.DestroyClearTextCopy(clearText);
ReportErrorEx(
m_hwnd,
IDS_12_PASSWORD_CHANGE_FAILED,
hr,
MB_OK | MB_ICONERROR,
apv,
1);
return;
}
hr = m_pDsObject->SetPassword(CComBSTR(clearText));
if (SUCCEEDED(hr))
{
hr = ModifyNetWareUserPassword(m_pDsObject, lpszPath, clearText);
}
// Zero out memory used for clear text copy and free its memory.
newPwd.DestroyClearTextCopy(clearText);
clearText = NULL;
if (SUCCEEDED(hr))
{
HRESULT hr2 = S_OK;
BOOL ForceChange = ChgDlg.GetChangePwd();
if (ForceChange)
{
//Check to see if the user password does not expire
BOOL bContinueToForceChange = TRUE;
IADs* pIADs = NULL;
HRESULT hr3 = m_pDsObject->QueryInterface(IID_IADs, OUT (void **)&pIADs);
if (SUCCEEDED(hr3))
{
ASSERT(pIADs != NULL);
CComVariant var;
hr3 = pIADs->Get(CComBSTR(gsz_userAccountControl), OUT &var);
if (SUCCEEDED(hr3))
{
ASSERT(var.vt == VT_I4);
if (var.lVal & UF_DONT_EXPIRE_PASSWD)
{
ReportErrorEx (m_hwnd,IDS_12_PASSWORD_DOES_NOT_EXPIRE,hr,
MB_OK | MB_ICONWARNING, apv, 1);
bContinueToForceChange = FALSE;
}
pIADs->Release();
}
}
// If password can expire then force the change at next logon
if (bContinueToForceChange)
{
IDirectoryObject * pIDSObject = NULL;
LPWSTR szPwdLastSet = L"pwdLastSet";
ADSVALUE ADsValuePwdLastSet = {ADSTYPE_LARGE_INTEGER, NULL};
ADS_ATTR_INFO AttrInfoPwdLastSet = {szPwdLastSet, ADS_ATTR_UPDATE,
ADSTYPE_LARGE_INTEGER,
&ADsValuePwdLastSet, 1};
ADsValuePwdLastSet.LargeInteger.QuadPart = 0;
hr2 = m_pDsObject->QueryInterface(IID_IDirectoryObject,
OUT (void **)&pIDSObject);
if (SUCCEEDED(hr2))
{
ASSERT(pIDSObject != NULL);
DWORD cAttrModified = 0;
hr2 = pIDSObject->SetObjectAttributes(&AttrInfoPwdLastSet,
1, &cAttrModified);
pIDSObject->Release();
if (FAILED(hr2))
{
ReportErrorEx(
m_hwnd,
IDS_PASSWORD_CHANGE_SUCCESS_FORCE_CHANGE_FAIL,
hr,
MB_OK | MB_ICONERROR, apv, 2);
}
}
}
}
if (SUCCEEDED(hr2))
{
ReportErrorEx (m_hwnd,IDS_12_PASSWORD_CHANGE_SUCCESSFUL,hr,
MB_OK | MB_ICONINFORMATION, apv, 1);
}
}
else
{
ReportErrorEx (m_hwnd,IDS_12_PASSWORD_CHANGE_FAILED,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
}
}
else
{
ReportErrorEx (m_hwnd,IDS_NEW_AND_CONFIRM_NOT_SAME,hr,
MB_OK | MB_ICONERROR, NULL, 0);
ChgDlg.Clear();
error = TRUE;
}
}
} while (error);
}
exit_gracefully:
return;
}
#define BREAK_ON_FAIL if (FAILED(hr)) { break; }
#define BREAK_AND_ASSERT_ON_FAIL if (FAILED(hr)) { ASSERT(FALSE); break; }
#define RETURN_AND_ASSERT_ON_FAIL if (FAILED(hr)) { ASSERT(FALSE); return; }
void CDSContextMenu::MoveObject()
{
// REVIEW_MARCOC: need to make sure the LDAP path has the SERVER or DOMAIN in it
// if called in the context of DS Admin, guard against property sheet open on this cookie
if (_WarningOnSheetsUp())
return;
// get the first path in the data object
ASSERT(m_objectNamesFormat.HasData());
// now do crack name to get root path for the browse dialog
CString szRootPath;
if (m_pCD != NULL)
{
szRootPath = m_pCD->GetBasePathsInfo()->GetProviderAndServerName();
szRootPath += m_pCD->GetRootPath();
}
else
{
LPCWSTR lpszObjPath = m_objectNamesFormat.GetName(0);
// make sure there's no strange escaping in the path
CComBSTR bstrPath;
CComBSTR bstrProvider;
CComBSTR bstrServer;
CPathCracker pathCracker;
pathCracker.Set(CComBSTR(lpszObjPath), ADS_SETTYPE_FULL);
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
pathCracker.put_EscapedMode(ADS_ESCAPEDMODE_OFF);
pathCracker.Retrieve( ADS_FORMAT_X500_DN, &bstrPath);
pathCracker.Retrieve( ADS_FORMAT_SERVER, &bstrServer);
pathCracker.Retrieve( ADS_FORMAT_PROVIDER, &bstrProvider);
LPWSTR pwszDomainPath;
HRESULT hr = CrackName(const_cast<LPWSTR>((LPCWSTR)bstrPath),
&pwszDomainPath,
GET_FQDN_DOMAIN_NAME,
m_hwnd);
if ((FAILED(hr)) || (HRESULT_CODE(hr) == DS_NAME_ERROR_NO_MAPPING))
{
TRACE(_T("CrackNames failed to get domain for %s.\n"),
lpszObjPath);
szRootPath = L"";
}
else
{
CPathCracker pathCrackerOther;
hr = pathCrackerOther.Set( CComBSTR(pwszDomainPath), ADS_SETTYPE_DN );
RETURN_AND_ASSERT_ON_FAIL;
hr = pathCrackerOther.Set( bstrProvider, ADS_SETTYPE_PROVIDER );
RETURN_AND_ASSERT_ON_FAIL;
hr = pathCrackerOther.Set( bstrServer, ADS_SETTYPE_SERVER );
RETURN_AND_ASSERT_ON_FAIL;
CComBSTR sbstrRootPath;
hr = pathCrackerOther.Retrieve( ADS_FORMAT_X500, &sbstrRootPath );
RETURN_AND_ASSERT_ON_FAIL;
szRootPath = sbstrRootPath;
}
if (pwszDomainPath != NULL)
::LocalFreeStringW(&pwszDomainPath);
}
CMultiselectMoveHandler moveHandler(m_pCD, m_hwnd, szRootPath);
HRESULT hr = moveHandler.Initialize(m_spDataObject, &m_objectNamesFormat,
&m_internalFormat);
ASSERT(SUCCEEDED(hr));
moveHandler.Move();
}
void CDSContextMenu::DeleteObject()
{
_ASSERTE(m_objectNamesFormat.HasData());
// if called in the context of DS Admin, guard against property sheet open on this cookie
if (_WarningOnSheetsUp())
return;
UINT nObjectCount = m_objectNamesFormat.GetCount();
if (nObjectCount == 0)
{
ASSERT(nObjectCount != 0);
return;
}
UINT nDeletedCount = 0;
PCWSTR* pszNameDelArr = 0;
PCWSTR* pszClassDelArr = 0;
DWORD* dwFlagsDelArr = 0;
DWORD* dwProviderFlagsDelArr = 0;
do // false loop
{
pszNameDelArr = new PCWSTR[nObjectCount];
pszClassDelArr = new PCWSTR[nObjectCount];
dwFlagsDelArr = new DWORD[nObjectCount];
dwProviderFlagsDelArr = new DWORD[nObjectCount];
if (!pszNameDelArr ||
!pszClassDelArr ||
!dwFlagsDelArr ||
!dwProviderFlagsDelArr)
{
break;
}
switch(nObjectCount)
{
case 1:
{
// single selection delete
CContextMenuSingleDeleteHandler deleteHandler(m_pCD, m_hwnd,
m_objectNamesFormat.GetName(0),
m_objectNamesFormat.GetClass(0),
m_objectNamesFormat.IsContainer(0),
this);
HRESULT hr = deleteHandler.Delete();
if (SUCCEEDED(hr) && (hr != S_FALSE))
{
nDeletedCount = 1;
pszNameDelArr[0] = m_objectNamesFormat.GetName(0);
pszClassDelArr[0] = m_objectNamesFormat.GetClass(0);
dwFlagsDelArr[0] = m_objectNamesFormat.GetFlags(0);
dwProviderFlagsDelArr[0] = m_objectNamesFormat.GetProviderFlags(0);
}
}
break;
default:
{
// multiple selection
CContextMenuMultipleDeleteHandler deleteHandler(m_pCD, m_hwnd, m_spDataObject,
&m_objectNamesFormat, this);
deleteHandler.Delete();
for (UINT k=0; k< nObjectCount; k++)
{
if (deleteHandler.WasItemDeleted(k))
{
pszNameDelArr[nDeletedCount] = m_objectNamesFormat.GetName(k);
pszClassDelArr[nDeletedCount] = m_objectNamesFormat.GetClass(k);
dwFlagsDelArr[nDeletedCount] = m_objectNamesFormat.GetFlags(k);
dwProviderFlagsDelArr[nDeletedCount] = m_objectNamesFormat.GetProviderFlags(k);
nDeletedCount++;
} // if
} // for
}
}; // switch
_NotifyDsFind((PCWSTR*)pszNameDelArr,
(PCWSTR*)pszClassDelArr,
dwFlagsDelArr,
dwProviderFlagsDelArr,
nDeletedCount);
} while (false);
if (pszNameDelArr)
{
delete[] pszNameDelArr;
pszNameDelArr = 0;
}
if (pszClassDelArr)
{
delete[] pszClassDelArr;
pszClassDelArr = 0;
}
if (dwFlagsDelArr)
{
delete[] dwFlagsDelArr;
dwFlagsDelArr = 0;
}
if (dwProviderFlagsDelArr)
{
delete[] dwProviderFlagsDelArr;
dwProviderFlagsDelArr = 0;
}
}
void CDSContextMenu::_NotifyDsFind(LPCWSTR* lpszNameDelArr,
LPCWSTR* lpszClassDelArr,
DWORD* dwFlagsDelArr,
DWORD* dwProviderFlagsDelArr,
UINT nDeletedCount)
{
if (nDeletedCount == 0)
{
// nothing to delete
return;
}
if (m_internalFormat.HasData())
{
// called from DS Admin directly, not from DS Find
return;
}
// ask DS Find about the notification interface
CComPtr<IQueryFrame> spIQueryFrame;
if ( !::SendMessage(m_hwnd, CQFWM_GETFRAME, 0, (LPARAM)&spIQueryFrame) )
{
// interface not found
return;
}
CComPtr<IDsQueryHandler> spDsQueryHandler;
HRESULT hr = spIQueryFrame->GetHandler(IID_IDsQueryHandler, (void **)&spDsQueryHandler);
if (FAILED(hr))
{
// interface not found
return;
}
// we finally have the interface, build the data structures
// figure out how much storage we need
DWORD cbStruct = sizeof(DSOBJECTNAMES) +
((nDeletedCount - 1) * sizeof(DSOBJECT));
size_t cbStorage = 0;
for (UINT index = 0; index < nDeletedCount; index++)
{
cbStorage += sizeof(WCHAR)*(wcslen(lpszNameDelArr[index])+1);
cbStorage += sizeof(WCHAR)*(wcslen(lpszClassDelArr[index])+1);
}
// allocate memory
LPDSOBJECTNAMES pDSObj = (LPDSOBJECTNAMES)::malloc(cbStruct + cbStorage);
if (pDSObj == NULL)
{
ASSERT(FALSE);
return;
}
// fill in the structs
pDSObj->clsidNamespace = m_CallerSnapin;
pDSObj->cItems = nDeletedCount;
DWORD NextOffset = cbStruct;
for (index = 0; index < nDeletedCount; index++)
{
pDSObj->aObjects[index].dwFlags = dwFlagsDelArr[index];
pDSObj->aObjects[index].dwProviderFlags = dwProviderFlagsDelArr[index];
pDSObj->aObjects[index].offsetName = NextOffset;
pDSObj->aObjects[index].offsetClass = static_cast<ULONG>(NextOffset +
(wcslen(lpszNameDelArr[index])+1) * sizeof(WCHAR));
_tcscpy((LPTSTR)((BYTE *)pDSObj + NextOffset), lpszNameDelArr[index]);
NextOffset += static_cast<ULONG>((wcslen(lpszNameDelArr[index]) + 1) * sizeof(WCHAR));
_tcscpy((LPTSTR)((BYTE *)pDSObj + NextOffset), lpszClassDelArr[index]);
NextOffset += static_cast<ULONG>((wcslen(lpszClassDelArr[index]) + 1) * sizeof(WCHAR));
}
// make the call
hr = spDsQueryHandler->UpdateView(DSQRVF_ITEMSDELETED, pDSObj);
::free(pDSObj);
}
HRESULT
CDSContextMenu::_Delete(LPCWSTR lpszPath, LPCWSTR lpszClass,
CString * csName)
{
CComBSTR strParent;
CComBSTR strThisRDN;
IADsContainer * pDSContainer = NULL;
IADs * pDSObject = NULL;
HRESULT hr = S_OK;
hr = DSAdminOpenObject(lpszPath,
IID_IADs,
(void **) &pDSObject,
TRUE /*bServer*/);
if (!SUCCEEDED(hr)) {
goto error;
}
hr = pDSObject->get_Parent(&strParent);
if (!SUCCEEDED(hr)) {
goto error;
}
hr = pDSObject->get_Name (&strThisRDN);
if (!SUCCEEDED(hr)) {
goto error;
}
pDSObject->Release();
pDSObject = NULL;
hr = DSAdminOpenObject(strParent,
IID_IADsContainer,
(void **) &pDSContainer,
TRUE /*bServer*/);
if (!SUCCEEDED(hr)) {
goto error;
}
hr = pDSContainer->Delete (CComBSTR(lpszClass),
CComBSTR(strThisRDN));
error:
if (pDSContainer)
pDSContainer->Release();
if (pDSObject)
pDSObject->Release();
if (FAILED(hr)) {
*csName = strThisRDN;
}
return hr;
}
HRESULT
CDSContextMenu::_DeleteSubtree(LPCWSTR lpszPath,
CString * csName)
{
HRESULT hr = S_OK;
IADsDeleteOps * pObj = NULL;
IADs * pObj2 = NULL;
hr = DSAdminOpenObject(lpszPath,
IID_IADsDeleteOps,
(void **)&pObj,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
TIMER(_T("Call to Deleteobject (to do subtree delete).\n"));
hr = pObj->DeleteObject(NULL); //flag is reserved by ADSI
TIMER(_T("Call to Deleteobject completed.\n"));
}
if (FAILED(hr)) {
CComBSTR strName;
HRESULT hr2 = pObj->QueryInterface (IID_IADs, (void **)&pObj2);
if (SUCCEEDED(hr2)) {
hr2 = pObj2->get_Name(&strName);
if (SUCCEEDED(hr2)) {
*csName = strName;
} else {
csName->LoadString (IDS_UNKNOWN);
}
}
}
if (pObj2) {
pObj2->Release();
}
if (pObj) {
pObj->Release();
}
return hr;
}
// code from JeffParh
NTSTATUS RetrieveRootDomainName( LPCWSTR lpcwszTargetDC, BSTR* pbstrRootDomainName )
{
if (NULL == pbstrRootDomainName)
{
ASSERT(FALSE);
return STATUS_INVALID_PARAMETER;
}
ASSERT( NULL == *pbstrRootDomainName );
NTSTATUS ntStatus = STATUS_SUCCESS;
LSA_HANDLE hPolicy = NULL;
POLICY_DNS_DOMAIN_INFO* pDnsDomainInfo = NULL;
do { // false loop
UNICODE_STRING unistrTargetDC;
if (NULL != lpcwszTargetDC)
{
unistrTargetDC.Length = (USHORT)(::lstrlen(lpcwszTargetDC)*sizeof(WCHAR));
unistrTargetDC.MaximumLength = unistrTargetDC.Length;
unistrTargetDC.Buffer = (LPWSTR)lpcwszTargetDC;
}
LSA_OBJECT_ATTRIBUTES oa;
ZeroMemory( &oa, sizeof(oa) );
ntStatus = LsaOpenPolicy(
(NULL != lpcwszTargetDC) ? &unistrTargetDC : NULL,
&oa,
POLICY_VIEW_LOCAL_INFORMATION,
&hPolicy
);
if ( !LSA_SUCCESS( ntStatus ) )
{
ASSERT(FALSE);
break;
}
ntStatus = LsaQueryInformationPolicy(
hPolicy,
PolicyDnsDomainInformation,
(PVOID*)&pDnsDomainInfo
);
if ( !LSA_SUCCESS( ntStatus ) )
{
ASSERT(FALSE);
break;
}
*pbstrRootDomainName = ::SysAllocStringLen(
pDnsDomainInfo->DnsForestName.Buffer,
pDnsDomainInfo->DnsForestName.Length / sizeof(WCHAR) );
if (NULL == *pbstrRootDomainName)
{
ntStatus = STATUS_NO_MEMORY;
break;
}
} while (false); // false loop
if (NULL != pDnsDomainInfo)
{
NTSTATUS ntstatus2 = LsaFreeMemory( pDnsDomainInfo );
ASSERT( LSA_SUCCESS(ntstatus2) );
}
if (NULL != hPolicy)
{
NTSTATUS ntstatus2 = LsaClose( hPolicy );
ASSERT( LSA_SUCCESS(ntstatus2) );
}
return ntStatus;
}
void AddMatchingNCs(
IN OUT CStringList& refstrlist,
IN const PADS_ATTR_INFO padsattrinfo1,
IN const PADS_ATTR_INFO padsattrinfo2 )
{
if ( !padsattrinfo1 || !padsattrinfo2 )
return;
for (DWORD iTarget = 0; iTarget < padsattrinfo1[0].dwNumValues; iTarget++)
{
LPWSTR lpszTargetNC = padsattrinfo1[0].pADsValues[iTarget].DNString;
ASSERT( NULL != lpszTargetNC );
bool fFound = false;
for (DWORD iSource = 0; iSource < padsattrinfo2[0].dwNumValues; iSource++)
{
LPWSTR lpszSourceNC = padsattrinfo2[0].pADsValues[iSource].DNString;
ASSERT( NULL != lpszSourceNC );
if ( !lstrcmpiW( lpszTargetNC, lpszSourceNC ) )
{
fFound = true;
break;
}
}
if (fFound)
refstrlist.AddHead( lpszTargetNC ); // CODEWORK can throw
}
}
HRESULT PrepareReplicaSyncParameters(
IN LPCWSTR strNTDSConnection,
IN BSTR bstrRootDomainName,
OUT BSTR* pbstrDsBindName,
OUT UUID* puuidSourceObjectGUID,
OUT CStringList& refstrlistCommonNCs,
OUT ULONG* pulDsSyncOptions,
OUT BSTR* pbstrFromServer
)
{
ASSERT( NULL != strNTDSConnection
&& NULL != bstrRootDomainName
&& NULL != pbstrDsBindName
&& NULL == *pbstrDsBindName
&& NULL != puuidSourceObjectGUID
&& refstrlistCommonNCs.IsEmpty()
&& NULL != pulDsSyncOptions
);
HRESULT hr = S_OK;
do { // false loop
CComPtr<IADs> spIADs;
// read attributes of nTDSConnection object
hr = DSAdminOpenObject(strNTDSConnection,
IID_IADs,
(void **) &spIADs,
TRUE /*bServer*/);
BREAK_ON_FAIL;
hr = GetStringAttr( spIADs, L"fromServer", pbstrFromServer);
BREAK_AND_ASSERT_ON_FAIL; // required attribute
spIADs.Release(); // method of CComPtr<>, also sets pointer to NULL
// get the path to the target nTDSDSA object
CPathCracker pathCracker;
hr = pathCracker.Set( const_cast<BSTR>(strNTDSConnection), ADS_SETTYPE_FULL );
BREAK_AND_ASSERT_ON_FAIL;
hr = pathCracker.RemoveLeafElement();
BREAK_AND_ASSERT_ON_FAIL;
CComBSTR sbstrTargetNTDSDSAPath;
hr = pathCracker.Retrieve( ADS_FORMAT_X500, &sbstrTargetNTDSDSAPath );
BREAK_AND_ASSERT_ON_FAIL;
// get the sitename for the target NTDSA object
hr = pathCracker.SetDisplayType( ADS_DISPLAY_VALUE_ONLY );
BREAK_AND_ASSERT_ON_FAIL;
CComBSTR sbstrTargetSite;
hr = pathCracker.GetElement( 3L, &sbstrTargetSite );
BREAK_AND_ASSERT_ON_FAIL;
hr = pathCracker.SetDisplayType( ADS_DISPLAY_FULL );
BREAK_AND_ASSERT_ON_FAIL;
// read objectGUID of the target nTDSDSA object
hr = DSAdminOpenObject(sbstrTargetNTDSDSAPath,
IID_IADs,
(void **) &spIADs,
TRUE /*bServer*/);
BREAK_ON_FAIL;
CComBSTR sbstrTargetObjectGUID;
hr = GetObjectGUID( spIADs, &sbstrTargetObjectGUID );
// The objectGUID attribute should be set for nTDSDSA objects
BREAK_AND_ASSERT_ON_FAIL;
// read hasMasterNCs of the target nTDSDSA object
Smart_PADS_ATTR_INFO spTargetMasterNCAttrs;
// 531591 JonN 2002/04/01 .NET Server domains use msDS-hasMasterNCs
hr = GetAttr( spIADs, L"msDS-hasMasterNCs", &spTargetMasterNCAttrs );
if (FAILED(hr) || !spTargetMasterNCAttrs)
{
// 531591 JonN 2002/04/01 W2K domains fallback to hasMasterNCs
spTargetMasterNCAttrs.Empty();
hr = GetAttr( spIADs, L"hasMasterNCs", &spTargetMasterNCAttrs );
}
// At least the hasMasterNCs attribute should be set for nTDSDSA objects,
// even those on W2K domains
BREAK_AND_ASSERT_ON_FAIL;
// read hasPartialReplicaNCs of the target nTDSDSA object
Smart_PADS_ATTR_INFO spTargetPartialNCAttrs;
(void) GetAttr( spIADs, L"hasPartialReplicaNCs", &spTargetPartialNCAttrs );
// The hasPartialReplicaNCs attribute may or may not be set for nTDSDSA objects
spIADs.Release(); // method of CComPtr<>, also sets pointer to NULL
/*
hr = spIADsPathname->RemoveLeafElement();
BREAK_AND_ASSERT_ON_FAIL;
CComBSTR sbstrTargetServerPath;
hr = spIADsPathname->Retrieve( ADS_FORMAT_X500, &sbstrTargetServerPath );
BREAK_AND_ASSERT_ON_FAIL;
hr = DSAdminOpenObject(sbstrTargetServerPath,
IID_IADs,
(void **) &spIADs,
TRUE);
BREAK_ON_FAIL;
CComVariant var;
hr = spIADs->Get(L"dNSHostName", &var);
BREAK_ON_FAIL; // can be missing for brand-new DCs
spIADs.Release(); // method of CComPtr<>, also sets pointer to NULL
ASSERT((var.vt == VT_BSTR) && var.bstrVal && *(var.bstrVal));
LPWSTR lpszDNSHostName = var.bstrVal;
*/
// get the path to the source nTDSDSA object
hr = pathCracker.Set(
(pbstrFromServer) ? *pbstrFromServer : NULL,
ADS_SETTYPE_DN );
BREAK_AND_ASSERT_ON_FAIL;
CComBSTR sbstrSourceNTDSDSAPath;
hr = pathCracker.Retrieve( ADS_FORMAT_X500, &sbstrSourceNTDSDSAPath );
BREAK_AND_ASSERT_ON_FAIL;
// get the sitename for the source NTDSA object
hr = pathCracker.SetDisplayType( ADS_DISPLAY_VALUE_ONLY );
BREAK_AND_ASSERT_ON_FAIL;
CComBSTR sbstrSourceSite;
hr = pathCracker.GetElement( 3L, &sbstrSourceSite );
BREAK_AND_ASSERT_ON_FAIL;
hr = pathCracker.SetDisplayType( ADS_DISPLAY_FULL );
BREAK_AND_ASSERT_ON_FAIL;
// determine whether the two DCs are in the same site
*pulDsSyncOptions = (lstrcmpi(sbstrSourceSite, sbstrTargetSite))
? DS_REPSYNC_ASYNCHRONOUS_OPERATION
: 0;
// read objectGUID of the source NTDSDSA object
hr = DSAdminOpenObject(sbstrSourceNTDSDSAPath,
IID_IADs,
(void **) &spIADs,
TRUE /*bServer*/);
BREAK_ON_FAIL;
hr = GetObjectGUID( spIADs, puuidSourceObjectGUID );
// The objectGUID attribute should be set for nTDSDSA objects
BREAK_AND_ASSERT_ON_FAIL;
// read hasMasterNCs of the source nTDSDSA object
Smart_PADS_ATTR_INFO spSourceMasterNCAttrs;
// 531591 JonN 2002/04/01 .NET Server domains use msDS-hasMasterNCs
hr = GetAttr( spIADs, L"msDS-hasMasterNCs", &spSourceMasterNCAttrs );
if (FAILED(hr) || !spSourceMasterNCAttrs)
{
// 531591 JonN 2002/04/01 W2K domains fallback to hasMasterNCs
spSourceMasterNCAttrs.Empty();
hr = GetAttr( spIADs, L"hasMasterNCs", &spSourceMasterNCAttrs );
}
// At least the hasMasterNCs attribute should be set for nTDSDSA objects,
// even those on W2K domains.
// read hasMasterNCs of the source nTDSDSA object
Smart_PADS_ATTR_INFO spSourcePartialNCAttrs;
(void) GetAttr( spIADs, L"hasPartialReplicaNCs", &spSourcePartialNCAttrs );
// The hasPartialReplicaNCs attribute may or may not be set for nTDSDSA objects
spIADs.Release(); // method of CComPtr<>, also sets pointer to NULL
// Determine which NCs the two NTDSDSAs have in common
AddMatchingNCs( refstrlistCommonNCs, spTargetMasterNCAttrs, spSourceMasterNCAttrs );
AddMatchingNCs( refstrlistCommonNCs, spTargetPartialNCAttrs, spSourceMasterNCAttrs );
AddMatchingNCs( refstrlistCommonNCs, spTargetPartialNCAttrs, spSourcePartialNCAttrs );
// Build the name of the inbound domain controller for this connection
CString csGUID( sbstrTargetObjectGUID );
ASSERT( L'{' == csGUID[0] && L'}' == csGUID[csGUID.GetLength()-1] );
CString csDC = csGUID.Mid( 1, csGUID.GetLength()-2 );
csDC += L"._msdcs.";
csDC += bstrRootDomainName;
*pbstrDsBindName = ::SysAllocString( csDC );
/*
*pbstrDsBindName = ::SysAllocString( lpszDNSHostName );
*/
if (NULL == *pbstrDsBindName)
{
hr = E_OUTOFMEMORY;
BREAK_AND_ASSERT_ON_FAIL;
}
} while (false); // false loop
return hr;
}
void CDSContextMenu::AddToGroup()
{
CWaitCursor waitcursor;
HRESULT hr = S_OK;
TRACE (_T("CDSContextMenu::AddToGroup\n"));
hr = AddDataObjListToGroup (&m_objectNamesFormat, m_hwnd, m_pCD);
return;
}
bool IsRPCError( NTSTATUS ntstatus )
{
switch (ntstatus)
{
case RPC_S_SERVER_UNAVAILABLE:
case HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE):
case ERROR_DS_DNS_LOOKUP_FAILURE:
case HRESULT_FROM_WIN32(ERROR_DS_DNS_LOOKUP_FAILURE):
case ERROR_NO_SUCH_DOMAIN:
case HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN):
case SEC_E_NO_AUTHENTICATING_AUTHORITY: // already an HRESULT
case ERROR_DOMAIN_CONTROLLER_NOT_FOUND:
case HRESULT_FROM_WIN32(ERROR_DOMAIN_CONTROLLER_NOT_FOUND):
return true;
default:
break;
}
return false;
}
// This level takes care of displaying the error messages
// CODEWORK should this try to replicate other domains to GCs?
void CDSContextMenu::ReplicateNow()
{
CWaitCursor waitcursor;
CComBSTR sbstrRootDomainName;
LPCWSTR lpcwszTargetDC = NULL;
if ( NULL != m_pCD && NULL != m_pCD->GetBasePathsInfo() )
lpcwszTargetDC = m_pCD->GetBasePathsInfo()->GetServerName();
NTSTATUS ntstatus = RetrieveRootDomainName( lpcwszTargetDC, &sbstrRootDomainName );
if ( !LSA_SUCCESS(ntstatus) )
{
// error in RetrieveRootDomainName against DSADMIN target DC
PVOID apv[1] = {(LPWSTR)(lpcwszTargetDC) };
(void) ReportErrorEx( m_hwnd,
(IsRPCError(ntstatus)) ? IDS_REPLNOW_1_RPC_PARAMLOAD_ERROR
: IDS_REPLNOW_1_PARAMLOAD_ERROR,
ntstatus, // CODEWORK
MB_OK | MB_ICONEXCLAMATION,
apv,
1,
IDS_REPLNOW_TITLE,
FALSE);
return;
}
CComBSTR sbstrFailingConnection;
CComBSTR sbstrFromServer;
CComBSTR sbstrFailingNC;
HRESULT hr = S_OK;
bool fSyncError = false;
ULONG ulOptionsUsed = 0;
// loop through the array of objects
UINT cCount;
for (cCount=0; cCount < m_objectNamesFormat.GetCount(); cCount++) {
if (wcscmp(m_objectNamesFormat.GetClass(cCount), L"nTDSConnection") !=0)
continue;
// get the replication parameters for this connection object
CComBSTR sbstrDsBindName;
UUID uuidSourceObjectGUID;
CStringList strlistCommonNCs;
ULONG ulDsSyncOptions = 0L;
sbstrFromServer.Empty();
sbstrFailingConnection = m_objectNamesFormat.GetName(cCount);
hr = PrepareReplicaSyncParameters(
sbstrFailingConnection,
sbstrRootDomainName,
&sbstrDsBindName,
&uuidSourceObjectGUID,
strlistCommonNCs,
&ulDsSyncOptions,
&sbstrFromServer
);
BREAK_ON_FAIL;
// now bind to the target DC
Smart_DsHandle shDS;
DWORD dwWinError = DsBind( sbstrDsBindName, // DomainControllerAddress
NULL, // DnsDomainName
&shDS );
if (ERROR_SUCCESS != dwWinError)
{
hr = HRESULT_FROM_WIN32(dwWinError);
ASSERT( FAILED(hr) );
break;
}
// sync all common naming contexts for this connection
CString strCommonNC;
POSITION pos = strlistCommonNCs.GetHeadPosition();
while (NULL != pos)
{
strCommonNC = strlistCommonNCs.GetNext( pos ) ;
ASSERT( 0 != strCommonNC.GetLength() );
dwWinError = DsReplicaSync( shDS,
const_cast<LPWSTR>((LPCTSTR)strCommonNC),
&uuidSourceObjectGUID,
ulDsSyncOptions );
if (ERROR_SUCCESS != dwWinError)
{
sbstrFailingNC = strCommonNC;
hr = HRESULT_FROM_WIN32(dwWinError);
ASSERT( FAILED(hr) );
break;
}
}
if ( FAILED(hr) )
{
fSyncError = true;
break;
}
ulOptionsUsed |= ulDsSyncOptions;
} // for
if ( SUCCEEDED(hr) )
{
(void) ReportMessageEx( m_hwnd,
(ulOptionsUsed & DS_REPSYNC_ASYNCHRONOUS_OPERATION)
? IDS_REPLNOW_SUCCEEDED_DELAYED
: IDS_REPLNOW_SUCCEEDED_IMMEDIATE,
MB_OK | MB_ICONINFORMATION,
NULL,
0,
IDS_REPLNOW_TITLE );
}
else
{
// JonN 3/30/00
// 6793: SITEREPL: ReplicateNow should provide more error information
// retrieve name of target DC
CComBSTR sbstrToServerRDN;
CPathCracker pathCracker;
HRESULT hr2 = pathCracker.Set(sbstrFailingConnection, ADS_SETTYPE_FULL);
ASSERT( SUCCEEDED(hr2) );
hr2 = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
ASSERT( SUCCEEDED(hr2) );
hr2 = pathCracker.GetElement(2, &sbstrToServerRDN);
ASSERT( SUCCEEDED(hr2) );
if (fSyncError)
{
// error in DsReplicaSync against connection target DC
// retrieve name of source DC
CComBSTR sbstrFromServerRDN;
hr2 = pathCracker.Set(sbstrFromServer, ADS_SETTYPE_DN);
ASSERT( SUCCEEDED(hr2) );
hr2 = pathCracker.GetElement(1, &sbstrFromServerRDN);
ASSERT( SUCCEEDED(hr2) );
// retrieve name of naming context
if (sbstrFailingNC && !wcsncmp(L"CN=",sbstrFailingNC,3))
{
hr2 = pathCracker.Set(sbstrFailingNC, ADS_SETTYPE_DN);
ASSERT( SUCCEEDED(hr2) );
hr2 = pathCracker.GetElement( 0, &sbstrFailingNC );
ASSERT( SUCCEEDED(hr2) );
} else {
LPWSTR pwzDomainNC = NULL;
hr2 = CrackName(sbstrFailingNC, &pwzDomainNC, GET_DNS_DOMAIN_NAME, NULL);
ASSERT( SUCCEEDED(hr2) && NULL != pwzDomainNC );
sbstrFailingNC = pwzDomainNC;
LocalFreeStringW(&pwzDomainNC);
}
PVOID apv[3] = { sbstrToServerRDN, sbstrFromServerRDN, sbstrFailingNC };
(void) ReportErrorEx( m_hwnd,
(IsRPCError(hr)) ? IDS_REPLNOW_3_RPC_FORCESYNC_ERROR
: IDS_REPLNOW_3_FORCESYNC_ERROR,
hr,
MB_OK | MB_ICONEXCLAMATION,
apv,
3,
IDS_REPLNOW_TITLE );
}
else
{
// error in PrepareReplicaSyncParameters against connection target DC
PVOID apv[1] = { sbstrToServerRDN };
(void) ReportErrorEx( m_hwnd,
(IsRPCError(hr)) ? IDS_REPLNOW_1_RPC_PARAMLOAD_ERROR
: IDS_REPLNOW_1_PARAMLOAD_ERROR,
hr,
MB_OK | MB_ICONEXCLAMATION,
apv,
1,
IDS_REPLNOW_TITLE );
}
}
}
void CDSContextMenu::CopyObject()
{
if (m_pCD != NULL)
{
m_pCD->_CopyDSObject(m_spDataObject);
}
}
void CDSContextMenu::_GetExtraInfo(LPDATAOBJECT pDataObj)
{
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
ASSERT(m_objectNamesFormat.HasData());
//we assume these are all the same
m_Advanced = (m_objectNamesFormat.GetProviderFlags(0) & DSPROVIDER_ADVANCED) != 0;
// set classes flag
for (UINT index = 0; index < m_objectNamesFormat.GetCount(); index++)
{
if (wcscmp(m_objectNamesFormat.GetClass(index), L"user") == 0
#ifdef INETORGPERSON
|| wcscmp(m_objectNamesFormat.GetClass(index), L"inetOrgPerson") == 0
#endif
)
m_fClasses |= Type_User;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"group") == 0)
m_fClasses |= Type_Group;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"computer") == 0)
m_fClasses |= Type_Computer;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"nTDSConnection") == 0)
m_fClasses |= Type_NTDSConnection;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"domainDNS") == 0)
m_fClasses |= Type_Domain;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"contact") == 0)
m_fClasses |= Type_Contact;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"group") == 0)
m_fClasses |= Type_Group;
else if (wcscmp(m_objectNamesFormat.GetClass(index), L"organizationalUnit") == 0)
m_fClasses |= Type_OU;
else
m_fClasses |= Type_Others;
} // for
// set classid
g_cfCoClass = (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
fmte.cfFormat = g_cfCoClass;
STGMEDIUM Stg;
Stg.tymed = TYMED_HGLOBAL;
//NTRAID#NTBUG9-571991-2002/03/10-jmessec Not checking failure of GlobalAlloc, possible leading to NULL ptr dereference
Stg.hGlobal = GlobalAlloc (GPTR, sizeof(CLSID));
HRESULT hr = pDataObj->GetDataHere(&fmte, &Stg);
if ( SUCCEEDED(hr) )
{
memcpy (&m_CallerSnapin, Stg.hGlobal, sizeof(GUID));
}
else
{
m_CallerSnapin = GUID_NULL;
}
GlobalFree(Stg.hGlobal);
//NTRAID#NTBUG9-571992-2002/03/10-jmessec Should not bypass the published MMC COM APIs in this manner; container
//structure could change in the future
// get HWND (MMC mainframe window)
g_cfParentHwnd = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_PARENTHWND);
fmte.cfFormat = g_cfParentHwnd;
Stg.tymed = TYMED_NULL;
if ( SUCCEEDED(pDataObj->GetData(&fmte, &Stg)) )
{
memcpy (&m_hwnd, Stg.hGlobal, sizeof(HWND));
ReleaseStgMedium(&Stg);
}
else
{
// need an HWND anyway
m_hwnd = ::GetActiveWindow();
}
TRACE(L"HWND = 0x%x\n", m_hwnd);
ASSERT((m_hwnd != NULL) && ::IsWindow(m_hwnd));
// get component data (if in the context of DS Admin)
g_cfComponentData = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_COMPDATA);
fmte.cfFormat = g_cfComponentData;
Stg.tymed = TYMED_NULL;
if ( SUCCEEDED(pDataObj->GetData(&fmte, &Stg)) )
{
memcpy (&m_pCD, Stg.hGlobal, sizeof(CDSComponentData*));
ReleaseStgMedium(&Stg);
} else
{
m_pCD = NULL;
}
// get component data (if in the context of DS Find)
if (m_pCD == NULL)
{
m_pCD = g_pCD;
}
}
BOOL
CDSContextMenu::_WarningOnSheetsUp()
{
// if called in the context of DS Admin, guard against property sheet open on this cookie
if ( (m_pCD != NULL) && m_internalFormat.HasData() )
{
return m_pCD->_WarningOnSheetsUp(&m_internalFormat);
}
return FALSE;
}
void
CDSContextMenu::Rename()
{
CThemeContextActivator activator;
HRESULT hr = S_OK;
INT_PTR answer = IDOK;
LPWSTR pszDomain = NULL;
LPWSTR pwzLocalDomain = NULL;
IDirectoryObject * pObj = NULL;
IADs * pObj2 = NULL;
IADs * pPartitions = NULL;
CString csLogin;
CString csTemp;
CString csSam;
CWaitCursor cwait;
BOOL error = FALSE;
BOOL fAccessDenied = FALSE;
LPWSTR pszNewName = NULL;
LPWSTR pszFirstName = NULL;
LPWSTR pszDispName = NULL;
LPWSTR pszSurName = NULL;
LPWSTR pszSAMName = NULL;
LPWSTR pszUPN = NULL;
BOOL NoRename = FALSE;
CString serverName;
CComVariant Var;
// guard against property sheet open on this cookie
if (_WarningOnSheetsUp())
return;
CString strClass = m_objectNamesFormat.GetClass(0);
if (strClass == L"user"
#ifdef INETORGPERSON
|| strClass == L"inetOrgPerson"
#endif
)
{
// rename user
CRenameUserDlg dlgRename(m_pCD);
LPWSTR pAttrNames[] = {L"distinguishedName",
L"userPrincipalName",
L"sAMAccountName",
L"givenName",
L"displayName",
L"sn",
L"cn"};
PADS_ATTR_INFO pAttrs = NULL;
ULONG cAttrs = 0;
CString szPath = m_objectNamesFormat.GetName(0);
hr = DSAdminOpenObject(szPath,
IID_IDirectoryObject,
(void **)&pObj,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
hr = pObj->GetObjectAttributes (pAttrNames, 7, &pAttrs, &cAttrs);
if (SUCCEEDED(hr)) {
for (UINT i = 0; i < cAttrs; i++) {
if (_wcsicmp (L"distinguishedName", pAttrs[i].pszAttrName) == 0) {
hr = CrackName (pAttrs[i].pADsValues->CaseIgnoreString,
&pszDomain, GET_NT4_DOMAIN_NAME, NULL);
if (SUCCEEDED(hr)) {
dlgRename.m_dldomain = pszDomain;
dlgRename.m_dldomain += L'\\';
}
// get the Domain of this object, need it later.
CComBSTR bsDN;
CPathCracker pathCracker;
pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
pathCracker.Set(CComBSTR(szPath), ADS_SETTYPE_FULL);
pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bsDN);
// NTRAID#NTBUG9-698115-2002/09/04-artm
// Get the server name we're connected to, we'll need it later for getting root domains.
// If we fail to get it that's okay, we'll still get the root domains
// unless the user is running dsadmin under "runas". If that's the case,
// the we won't show parent domains (but everything else will work).
CComBSTR tempServerName;
hr = pathCracker.Retrieve(ADS_FORMAT_SERVER, &tempServerName);
if (SUCCEEDED(hr))
{
serverName = tempServerName;
}
// get the NT 5 (dns) domain name
TRACE(L"CrackName(%s, &pwzLocalDomain, GET_DNS_DOMAIN_NAME, NULL);\n", bsDN);
hr = CrackName(bsDN, &pwzLocalDomain, GET_DNS_DOMAIN_NAME, NULL);
TRACE(L"CrackName returned hr = 0x%x, pwzLocalDomain = <%s>\n", hr, pwzLocalDomain);
}
if (_wcsicmp (L"userPrincipalName", pAttrs[i].pszAttrName) == 0) {
csTemp = pAttrs[i].pADsValues->CaseIgnoreString;
INT loc = csTemp.Find (L'@');
if (loc > 0) {
dlgRename.m_login = csTemp.Left(loc);
dlgRename.m_domain = csTemp.Right (csTemp.GetLength() - loc);
} else {
dlgRename.m_login = csTemp;
ASSERT (0 && L"can't find @ in upn");
}
}
if (_wcsicmp (L"sAMAccountName", pAttrs[i].pszAttrName) == 0) {
dlgRename.m_samaccountname = pAttrs[i].pADsValues->CaseIgnoreString;
}
if (_wcsicmp (L"givenName", pAttrs[i].pszAttrName) == 0) {
dlgRename.m_first = pAttrs[i].pADsValues->CaseIgnoreString;
}
if (_wcsicmp (L"displayName", pAttrs[i].pszAttrName) == 0) {
dlgRename.m_displayname = pAttrs[i].pADsValues->CaseIgnoreString;
}
if (_wcsicmp (L"sn", pAttrs[i].pszAttrName) == 0) {
dlgRename.m_last = pAttrs[i].pADsValues->CaseIgnoreString;
}
if (_wcsicmp (L"cn", pAttrs[i].pszAttrName) == 0) {
dlgRename.m_cn = pAttrs[i].pADsValues->CaseIgnoreString;
dlgRename.m_oldcn = dlgRename.m_cn;
}
}
}
// get UPN suffixes from this OU, if present
IADs * pObjADs = NULL;
IADs * pCont = NULL;
BSTR bsParentPath;
CStringList UPNs;
CString strAtDomain;
hr = pObj->QueryInterface (IID_IADs, (void **)&pObjADs);
ASSERT (SUCCEEDED(hr));
hr = pObjADs->get_Parent(&bsParentPath);
ASSERT (SUCCEEDED(hr));
hr = DSAdminOpenObject(bsParentPath,
IID_IADs,
(void **)&pCont,
TRUE /*bServer*/);
CComVariant sVar;
hr = pCont->Get ( CComBSTR(L"uPNSuffixes"), &sVar);
if (SUCCEEDED(hr)) {
hr = HrVariantToStringList (IN sVar, UPNs);
if (SUCCEEDED(hr)) {
POSITION pos = UPNs.GetHeadPosition();
CString csSuffix;
while (pos != NULL) {
csSuffix = L"@";
csSuffix += UPNs.GetNext(INOUT pos);
TRACE(_T("UPN suffix: %s\n"), csSuffix);
if (wcscmp (csSuffix, dlgRename.m_domain) &&
!dlgRename.m_domains.Find(csSuffix))
{
dlgRename.m_domains.AddTail (csSuffix);
}
}
}
} else {// now get the domain options
CComPtr<IDsBrowseDomainTree> pDsDomains = NULL;
PDOMAIN_TREE pNewDomains = NULL;
do // false while loop
{
hr = ::CoCreateInstance(CLSID_DsDomainTreeBrowser,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDsBrowseDomainTree,
(LPVOID*)&pDsDomains);
if (FAILED(hr) || pDsDomains == NULL)
{
ASSERT(SUCCEEDED(hr) && pDsDomains != NULL);
break;
}
// NTRAID#NTBUG9-698115-2002/09/04-artm
// Ensure that the domains we request are correctly scoped.
hr = pDsDomains->SetComputer(serverName, NULL, NULL);
if (FAILED(hr))
{
ASSERT(SUCCEEDED(hr));
break;
}
hr = pDsDomains->GetDomains(&pNewDomains, 0);
if (FAILED(hr) || pNewDomains == NULL)
{
// Only expect to get here with failed hresult.
ASSERT(FAILED(hr));
break;
}
for (UINT index = 0; index < pNewDomains->dwCount; index++)
{
if (pNewDomains->aDomains[index].pszTrustParent == NULL)
{
// Add the root domain only if it is a substring of the current
// domain.
//
size_t cchRoot = wcslen(pNewDomains->aDomains[index].pszName);
PWSTR pRoot = pwzLocalDomain + wcslen(pwzLocalDomain) - cchRoot;
if (pRoot >= pwzLocalDomain &&
!_wcsicmp(pRoot, pNewDomains->aDomains[index].pszName))
{
strAtDomain = "@";
strAtDomain += pNewDomains->aDomains[index].pszName;
if (_wcsicmp(pNewDomains->aDomains[index].pszName, dlgRename.m_domain) &&
!dlgRename.m_domains.Find(strAtDomain))
{
dlgRename.m_domains.AddTail (strAtDomain);
}
}
}
} // end for loop
} while (false);
if (pDsDomains != NULL && pNewDomains != NULL)
{
pDsDomains->FreeDomains(&pNewDomains);
pNewDomains = NULL;
}
// If the local domain isn't the same as the root, then add it
CString strAtLocalDomain = L"@";
strAtLocalDomain += pwzLocalDomain;
if (!dlgRename.m_domains.Find(strAtLocalDomain))
{
dlgRename.m_domains.AddTail(strAtLocalDomain);
}
LocalFreeStringW(&pszDomain);
// get UPN suffixes
CString csPartitions;
CStringList UPNsToo;
// get config path from main object
csPartitions = m_pCD->GetBasePathsInfo()->GetProviderAndServerName();
csPartitions += L"CN=Partitions,";
csPartitions += m_pCD->GetBasePathsInfo()->GetConfigNamingContext();
hr = DSAdminOpenObject(csPartitions,
IID_IADs,
(void **)&pPartitions,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
CComVariant sVarToo;
hr = pPartitions->Get ( CComBSTR(L"uPNSuffixes"), &sVarToo);
if (SUCCEEDED(hr)) {
hr = HrVariantToStringList (IN sVarToo, UPNsToo);
if (SUCCEEDED(hr)) {
POSITION pos = UPNsToo.GetHeadPosition();
CString csSuffix;
while (pos != NULL) {
csSuffix = L"@";
csSuffix += UPNsToo.GetNext(INOUT pos);
TRACE(_T("UPN suffix: %s\n"), csSuffix);
if (wcscmp (csSuffix, dlgRename.m_domain) &&
!dlgRename.m_domains.Find(csSuffix))
{
dlgRename.m_domains.AddTail (csSuffix);
}
}
}
}
pPartitions->Release();
}
}
error = TRUE;
while ((error) && (!fAccessDenied))
{
answer = dlgRename.DoModal();
if (answer == IDOK)
{
ADSVALUE avUPN = {ADSTYPE_CASE_IGNORE_STRING, NULL};
ADS_ATTR_INFO aiUPN = {L"userPrincipalName", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &avUPN, 1};
ADSVALUE avSAMName = {ADSTYPE_CASE_IGNORE_STRING, NULL};
ADS_ATTR_INFO aiSAMName = {L"sAMAccountName", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &avSAMName, 1};
ADSVALUE avGiven = {ADSTYPE_CASE_IGNORE_STRING, NULL};
ADS_ATTR_INFO aiGiven = {L"givenName", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &avGiven, 1};
ADSVALUE avSurName = {ADSTYPE_CASE_IGNORE_STRING, NULL};
ADS_ATTR_INFO aiSurName = {L"sn", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &avSurName, 1};
ADSVALUE avDispName = {ADSTYPE_CASE_IGNORE_STRING, NULL};
ADS_ATTR_INFO aiDispName = {L"displayName", ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING, &avDispName, 1};
ADS_ATTR_INFO rgAttrs[5];
ULONG cModified = 0;
cAttrs = 0;
if (!dlgRename.m_login.IsEmpty() &&
!dlgRename.m_domain.IsEmpty())
{
dlgRename.m_login.TrimRight();
dlgRename.m_login.TrimLeft();
dlgRename.m_domain.TrimRight();
dlgRename.m_domain.TrimLeft();
csTemp = (dlgRename.m_login + dlgRename.m_domain);
pszUPN = new WCHAR[wcslen(csTemp) + 1];
if (pszUPN)
{
wcscpy (pszUPN, csTemp);
avUPN.CaseIgnoreString = pszUPN;
}
}
else
{
aiUPN.dwControlCode = ADS_ATTR_CLEAR;
}
rgAttrs[cAttrs++] = aiUPN;
// test UPN for duplication
// test UPN for duplication
// validate UPN with GC before doing the put.
BOOL fDomainSearchFailed = FALSE;
BOOL fGCSearchFailed = FALSE;
BOOL dup = FALSE;
CString strFilter;
LPWSTR pAttributes[1] = {L"cn"};
IDirectorySearch * pGCObj = NULL;
CDSSearch DSS (m_pCD->m_pClassCache, m_pCD);
hr = DSPROP_GetGCSearchOnDomain(pwzLocalDomain,
IID_IDirectorySearch,
(void **)&pGCObj);
if (FAILED(hr)) {
fGCSearchFailed = TRUE;
} else {
DSS.Init (pGCObj);
LPWSTR pUserAttributes[1] = {L"cn"};
strFilter = L"(userPrincipalName=";
strFilter += pszUPN;
strFilter += L")";
DSS.SetAttributeList (pUserAttributes, 1);
DSS.SetFilterString ((LPWSTR)(LPCWSTR)strFilter);
DSS.SetSearchScope (ADS_SCOPE_SUBTREE);
DSS.DoQuery();
hr = DSS.GetNextRow();
dup = FALSE;
while ((hr == S_OK) && (dup == FALSE)) { // this means a row was returned, so we're dup
ADS_SEARCH_COLUMN Col;
hr = DSS.GetColumn(pUserAttributes[0], &Col);
if (_wcsicmp(Col.pADsValues->CaseIgnoreString, dlgRename.m_oldcn)) {
dup = TRUE;
ReportErrorEx (m_hwnd, IDS_UPN_DUP, hr,
MB_OK, NULL, 0);
}
hr = DSS.GetNextRow();
}
if (hr != S_ADS_NOMORE_ROWS) {
fGCSearchFailed = TRUE;
}
}
HRESULT hr2 = S_OK;
if (dup)
continue;
else {
CString strInitPath = L"LDAP://";
strInitPath += pwzLocalDomain;
TRACE(_T("Initialize Domain search object with: %s...\n"), strInitPath);
hr2 = DSS.Init (strInitPath);
if (SUCCEEDED(hr2)) {
LPWSTR pAttributes2[1] = {L"cn"};
strFilter = L"(userPrincipalName=";
strFilter += pszUPN;
strFilter += L")";
TRACE(_T("searching current domain for %s...\n"), pszUPN);
DSS.SetAttributeList (pAttributes2, 1);
DSS.SetFilterString ((LPWSTR)(LPCWSTR)strFilter);
DSS.SetSearchScope (ADS_SCOPE_SUBTREE);
DSS.DoQuery();
hr2 = DSS.GetNextRow();
TRACE(_T("done searching current domain for %s...\n"), pszUPN);
}
while ((hr2 == S_OK) && (dup == FALSE)) { // this means a row was returned, so we're dup
ADS_SEARCH_COLUMN Col;
HRESULT hr3 = DSS.GetColumn(pAttributes[0], &Col);
ASSERT(hr3 == S_OK);
if (_wcsicmp(Col.pADsValues->CaseIgnoreString, dlgRename.m_oldcn)) {
dup = TRUE;
ReportErrorEx (m_hwnd, IDS_UPN_DUP, hr,
MB_OK, NULL, 0);
}
hr2 = DSS.GetNextRow();
}
if (hr2 != S_ADS_NOMORE_ROWS) { // oops, had another problem
fDomainSearchFailed = TRUE;
}
}
if (dup)
continue;
else {
if (fDomainSearchFailed || fGCSearchFailed) {
HRESULT hrSearch = S_OK;
if (fDomainSearchFailed) {
hrSearch = hr2;
} else {
hrSearch = hr;
}
ReportErrorEx (m_hwnd,IDS_UPN_SEARCH_FAILED2,hrSearch,
MB_OK | MB_ICONWARNING, NULL, 0);
}
}
if (pGCObj) {
pGCObj->Release();
pGCObj = NULL;
}
//NTRAID#NTBUG9-569671-2002/03/10-jmessec this should be + 1, not + sizeof(WCHAR)
pszNewName = new WCHAR[wcslen(dlgRename.m_cn) + 1];
dlgRename.m_cn.TrimRight();
dlgRename.m_cn.TrimLeft();
wcscpy (pszNewName, dlgRename.m_cn);
if (dlgRename.m_cn == dlgRename.m_oldcn)
NoRename = TRUE;
if (!dlgRename.m_displayname.IsEmpty()) {
dlgRename.m_displayname.TrimLeft();
dlgRename.m_displayname.TrimRight();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec this should be + 1, not + sizeof(WCHAR)
pszDispName = new WCHAR[wcslen(dlgRename.m_displayname) + 1];
wcscpy (pszDispName, dlgRename.m_displayname);
avDispName.CaseIgnoreString = pszDispName;
} else {
aiDispName.dwControlCode = ADS_ATTR_CLEAR;
}
rgAttrs[cAttrs++] = aiDispName;
if (!dlgRename.m_first.IsEmpty()) {
dlgRename.m_first.TrimLeft();
dlgRename.m_first.TrimRight();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec this should be + 1, not + sizeof(WCHAR)
pszFirstName = new WCHAR[wcslen(dlgRename.m_first) + 1];
wcscpy (pszFirstName, dlgRename.m_first);
avGiven.CaseIgnoreString = pszFirstName;
} else {
aiGiven.dwControlCode = ADS_ATTR_CLEAR;
}
rgAttrs[cAttrs++] = aiGiven;
if (!dlgRename.m_last.IsEmpty()) {
dlgRename.m_last.TrimLeft();
dlgRename.m_last.TrimRight();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec sizeof(WCHAR) below should be 1
pszSurName = new WCHAR[wcslen(dlgRename.m_last) + 1];
wcscpy (pszSurName, dlgRename.m_last);
avSurName.CaseIgnoreString = pszSurName;
} else {
aiSurName.dwControlCode = ADS_ATTR_CLEAR;
}
rgAttrs[cAttrs++] = aiSurName;
if (!dlgRename.m_samaccountname.IsEmpty()) {
dlgRename.m_samaccountname.TrimLeft();
dlgRename.m_samaccountname.TrimRight();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec sizeof(WCHAR) below should be 1
pszSAMName = new WCHAR[wcslen(dlgRename.m_samaccountname) + 1];
wcscpy (pszSAMName, dlgRename.m_samaccountname);
avSAMName.CaseIgnoreString = pszSAMName;
} else {
aiSAMName.dwControlCode = ADS_ATTR_CLEAR;
}
rgAttrs[cAttrs++] = aiSAMName;
hr = pObj->SetObjectAttributes (rgAttrs, cAttrs, &cModified);
if (FAILED(hr)) {
if (hr == E_ACCESSDENIED) {
fAccessDenied = TRUE;
NoRename = TRUE;
} else {
ReportErrorEx (m_hwnd, IDS_NAME_CHANGE_FAILED, hr,
MB_OK|MB_ICONERROR, NULL, 0, TRUE);
}
} else {
error = FALSE;
}
} else {
error = FALSE;
}
}
} else {
answer = IDCANCEL;
PVOID apv[1] = {(BSTR)(LPWSTR)(LPCWSTR)m_objectNamesFormat.GetName(0)};
ReportErrorEx (m_hwnd,IDS_12_USER_OBJECT_NOT_ACCESSABLE,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
} else if (strClass == L"group") {
CRenameGroupDlg * pdlgRename = new CRenameGroupDlg;
CString szPath;
szPath = m_objectNamesFormat.GetName(0);
hr = DSAdminOpenObject(szPath,
IID_IADs,
(void **)&pObj2,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
hr = pObj2->Get (CComBSTR(L"sAMAccountName"), &Var);
ASSERT (SUCCEEDED(hr));
csSam = Var.bstrVal;
if (strClass == L"computer") {
INT loc = csSam.Find(L"$");
if (loc > 0) {
csSam = csSam.Left(loc);
}
}
hr = pObj2->Get (CComBSTR(L"cn"), &Var);
ASSERT (SUCCEEDED(hr));
pdlgRename->m_cn = Var.bstrVal;
// figure out group type
if (strClass == L"group") {
CComVariant varType;
hr = pObj2->Get(CComBSTR(L"groupType"), &varType);
ASSERT(SUCCEEDED(hr));
INT GroupType = (varType.lVal & ~GROUP_TYPE_SECURITY_ENABLED);
if (GroupType == GROUP_TYPE_RESOURCE_GROUP) {
pdlgRename->m_samtextlimit = 64;
}
}
pdlgRename->m_samaccountname = csSam;
error = TRUE;
while ((error) && (!fAccessDenied))
{
answer = pdlgRename->DoModal();
if (answer == IDOK)
{
pdlgRename->m_cn.TrimRight();
pdlgRename->m_cn.TrimLeft();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec 1 * sizeof(WCHAR) below should be 1
pszNewName = new WCHAR[wcslen(pdlgRename->m_cn) + (1 * sizeof(WCHAR))];
wcscpy (pszNewName, pdlgRename->m_cn);
Var.vt = VT_BSTR;
pdlgRename->m_samaccountname.TrimLeft();
pdlgRename->m_samaccountname.TrimRight();
csSam = pdlgRename->m_samaccountname;
if (strClass == L"computer")
{
csSam += L"$";
}
Var.bstrVal = SysAllocString(csSam);
hr = pObj2->Put (CComBSTR(L"sAMAccountName"), Var);
ASSERT (SUCCEEDED(hr));
if (FAILED(hr)) {
continue;
}
hr = pObj2->SetInfo();
if (FAILED(hr)) {
if (hr == E_ACCESSDENIED) {
fAccessDenied = TRUE;
NoRename = TRUE;
} else {
ReportErrorEx (m_hwnd, IDS_NAME_CHANGE_FAILED, hr,
MB_OK|MB_ICONERROR, NULL, 0, TRUE);
}
} else {
error = FALSE;
}
} else {
error = FALSE;
}
}
} else {
answer = IDCANCEL;
}
if (pdlgRename) {
delete pdlgRename;
}
} else if (strClass == L"contact") {
// rename contact
CRenameContactDlg dlgRename;
CString szPath;
szPath = m_objectNamesFormat.GetName(0);
hr = DSAdminOpenObject(szPath,
IID_IADs,
(void **)&pObj2,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
hr = pObj2->Get (CComBSTR(L"givenName"), &Var);
ASSERT (SUCCEEDED(hr) || (hr == E_ADS_PROPERTY_NOT_FOUND));
if (SUCCEEDED(hr)) {
dlgRename.m_first = Var.bstrVal;
}
hr = pObj2->Get (CComBSTR(L"sn"), &Var);
ASSERT (SUCCEEDED(hr) || (hr == E_ADS_PROPERTY_NOT_FOUND));
if (SUCCEEDED(hr)) {
dlgRename.m_last = Var.bstrVal;
}
hr = pObj2->Get (CComBSTR(L"displayName"), &Var);
ASSERT (SUCCEEDED(hr) || (hr == E_ADS_PROPERTY_NOT_FOUND));
if (SUCCEEDED(hr)) {
dlgRename.m_disp = Var.bstrVal;
}
hr = pObj2->Get (CComBSTR(L"cn"), &Var);
ASSERT (SUCCEEDED(hr) || (hr == E_ADS_PROPERTY_NOT_FOUND));
if (SUCCEEDED(hr)) {
dlgRename.m_cn = Var.bstrVal;
}
error = TRUE;
while ((error) && (!fAccessDenied))
{
answer = dlgRename.DoModal();
if (answer == IDOK)
{
dlgRename.m_cn.TrimRight();
dlgRename.m_cn.TrimLeft();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec 1 * sizeof(WCHAR) below should be 1
pszNewName = new WCHAR[wcslen(dlgRename.m_cn) + (1 * sizeof(WCHAR))];
wcscpy (pszNewName, dlgRename.m_cn);
Var.vt = VT_BSTR;
if (!dlgRename.m_first.IsEmpty())
{
dlgRename.m_first.TrimLeft();
dlgRename.m_first.TrimRight();
Var.bstrVal = SysAllocString (dlgRename.m_first);
hr = pObj2->Put (CComBSTR(L"givenName"), Var);
ASSERT (SUCCEEDED(hr));
}
if (!dlgRename.m_last.IsEmpty()) {
dlgRename.m_last.TrimLeft();
dlgRename.m_last.TrimRight();
Var.bstrVal = SysAllocString(dlgRename.m_last);
hr = pObj2->Put (CComBSTR(L"sn"), Var);
ASSERT (SUCCEEDED(hr));
}
if (!dlgRename.m_disp.IsEmpty()) {
dlgRename.m_disp.TrimLeft();
dlgRename.m_disp.TrimRight();
Var.bstrVal = SysAllocString(dlgRename.m_disp);
hr = pObj2->Put (CComBSTR(L"displayName"), Var);
ASSERT (SUCCEEDED(hr));
}
hr = pObj2->SetInfo();
if (FAILED(hr)) {
if (hr == E_ACCESSDENIED) {
fAccessDenied = TRUE;
NoRename = TRUE;
} else {
ReportErrorEx (m_hwnd, IDS_NAME_CHANGE_FAILED, hr,
MB_OK|MB_ICONERROR, NULL, 0, TRUE);
}
} else {
error = FALSE;
}
} else {
error = FALSE;
}
}
} else {
answer = IDCANCEL;
}
} else {
// need generic dialog here.
CRenameGenericDlg dlgRename (CWnd::FromHandle(m_hwnd));
CString szPath;
szPath = m_objectNamesFormat.GetName(0);
hr = DSAdminOpenObject(szPath,
IID_IADs,
(void **)&pObj2,
TRUE /*bServer*/);
if (SUCCEEDED(hr)) {
CDSClassCacheItemBase* pItem = NULL;
pItem = m_pCD->m_pClassCache->FindClassCacheItem(m_pCD, (LPCWSTR)strClass, szPath);
ASSERT (pItem != NULL);
//get the naming attribute
CString csNewAttrName;
csNewAttrName = pItem->GetNamingAttribute();
hr = pObj2->Get (CComBSTR(csNewAttrName), &Var);
ASSERT (SUCCEEDED(hr) || (hr == E_ADS_PROPERTY_NOT_FOUND));
if (SUCCEEDED(hr)) {
dlgRename.m_cn = Var.bstrVal;
}
//NTRAID#NTBUG9-571993-2002/03/10-jmessec what is all of this while (error) rigamarole for? it never loops
error = TRUE;
while (error)
{
CThemeContextActivator activator;
answer = dlgRename.DoModal();
if (answer == IDOK)
{
dlgRename.m_cn.TrimRight();
dlgRename.m_cn.TrimLeft();
//NTRAID#NTBUG9-569671-2002/03/10-jmessec 1 * sizeof(WCHAR) below should be 1
pszNewName = new WCHAR[wcslen(dlgRename.m_cn) + (1 * sizeof(WCHAR))];
wcscpy (pszNewName, dlgRename.m_cn);
error = FALSE;
}
else
{
error = FALSE;
}
}
}
}
if ((answer == IDOK) && (error == FALSE) && (NoRename == FALSE)) {
CString csObjectPath = m_objectNamesFormat.GetName(0);
CDSClassCacheItemBase* pItem = NULL;
pItem = m_pCD->m_pClassCache->FindClassCacheItem(m_pCD, (LPCWSTR)strClass, csObjectPath);
ASSERT (pItem != NULL);
// get the new name in the form "cn=foo" or "ou=foo"
CString csNewAttrName;
csNewAttrName = pItem->GetNamingAttribute();
csNewAttrName += L"=";
csNewAttrName += pszNewName;
TRACE(_T("_RenameObject: Attributed name is %s.\n"), csNewAttrName);
// bind to object
IADs *pDSObject = NULL;
hr = DSAdminOpenObject(csObjectPath,
IID_IADs,
(void **)&pDSObject,
TRUE /*bServer*/);
if (!SUCCEEDED(hr)) {
goto error;
}
BSTR bsParentPath;
// get the path of the object container
hr = pDSObject->get_Parent (&bsParentPath);
if (!SUCCEEDED(hr)) {
goto error;
}
pDSObject->Release();
pDSObject = NULL;
IADsContainer * pContainer = NULL;
// bind to the object container
hr = DSAdminOpenObject(bsParentPath,
IID_IADsContainer,
(void **)&pContainer,
TRUE /*bServer*/);
if (!SUCCEEDED(hr)) {
goto error;
}
// build the new LDAP path
CString csNewNamingContext, csNewPath, szPath;
BSTR bsEscapedName;
csNewNamingContext = csNewAttrName;
csNewNamingContext += L",";
StripADsIPath(bsParentPath, szPath);
csNewNamingContext += szPath;
m_pCD->GetBasePathsInfo()->ComposeADsIPath(csNewPath, csNewNamingContext);
// create a transaction object, the destructor will call End() on it
CDSNotifyHandlerTransaction transaction(m_pCD);
transaction.SetEventType(DSA_NOTIFY_REN);
// start the transaction
hr = transaction.Begin(m_objectNamesFormat.GetName(0),
m_objectNamesFormat.GetClass(0),
m_objectNamesFormat.IsContainer(0),
csNewPath,
m_objectNamesFormat.GetClass(0),
m_objectNamesFormat.IsContainer(0));
// ask for confirmation
if (transaction.NeedNotifyCount() > 0)
{
CString szMessage, szAssocData;
szMessage.LoadString(IDS_CONFIRM_RENAME);
szAssocData.LoadString(IDS_EXTENS_RENAME);
CConfirmOperationDialog dlg(::GetParent(m_hwnd), &transaction);
dlg.SetStrings(szMessage, szAssocData);
if (IDNO == dlg.DoModal())
{
transaction.End();
hr = S_OK;
goto error;
}
}
CPathCracker pathCracker;
hr = pathCracker.GetEscapedElement(0, //reserved
(BSTR)(LPCWSTR)csNewAttrName,
&bsEscapedName);
if (FAILED(hr))
goto error;
IDispatch * pDispObj = NULL;
// do the actual rename
hr = pContainer->MoveHere(CComBSTR(csObjectPath),
CComBSTR(bsEscapedName),
&pDispObj);
if (SUCCEEDED(hr) && (hr != S_FALSE)) {
// let extensions know
transaction.Notify(0);
// send notify to diz
}
if (pDispObj) {
pDispObj->Release();
}
}
if (fAccessDenied) {
PVOID apv[1] = {(LPWSTR)m_objectNamesFormat.GetName(0)};
ReportErrorEx(::GetParent(m_hwnd),IDS_12_RENAME_NOT_ALLOWED,hr,
MB_OK | MB_ICONERROR, apv, 1);
}
error:
// transaction.End() will be called by the transaction's destructor
if (pwzLocalDomain) {
LocalFreeStringW(&pwzLocalDomain);
}
if (pszNewName) {
delete[] pszNewName;
}
if (pszFirstName) {
delete[] pszFirstName;
}
if (pszDispName) {
delete[] pszDispName;
}
if (pszSurName) {
delete[] pszSurName;
}
if (pszSAMName){
delete[] pszSAMName;
}
if (pszUPN) {
delete[] pszUPN;
}
if (pObj) {
pObj->Release();
}
if (pObj2) {
pObj2->Release();
}
}