798 lines
18 KiB
C++
798 lines
18 KiB
C++
|
// Copyright (C) 2000 Microsoft Corporation
|
||
|
//
|
||
|
// Non-domain Naming Context checking code
|
||
|
//
|
||
|
// 13 July 2000 sburns, from code supplied by jeffparh
|
||
|
|
||
|
|
||
|
|
||
|
#include "headers.hxx"
|
||
|
#include "state.hpp"
|
||
|
#include "resource.h"
|
||
|
#include "NonDomainNc.hpp"
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef LOGGING_BUILD
|
||
|
#define LOG_LDAP(msg, ldap) LOG(msg); LOG(String::format(L"LDAP error %1!ld!", (ldap)))
|
||
|
#else
|
||
|
#define LOG_LDAP(msg, ldap)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT
|
||
|
LdapToHresult(int ldapError)
|
||
|
{
|
||
|
// CODEWORK: I'm told that ldap_get_option for LDAP_OPT_SERVER_ERROR or
|
||
|
// LDAP_OPT_SERVER_EXT_ERROR (or perhaps LDAP_OPT_ERROR_STRING?) will give
|
||
|
// an error result with "higher fidelity"
|
||
|
|
||
|
return Win32ToHresult(::LdapMapErrorToWin32(ldapError));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// provided by jeffparh
|
||
|
|
||
|
DWORD
|
||
|
IsLastReplicaOfNC(
|
||
|
IN LDAP * hld,
|
||
|
IN LPWSTR pszConfigNC,
|
||
|
IN LPWSTR pszNC,
|
||
|
IN LPWSTR pszNtdsDsaDN,
|
||
|
OUT BOOL * pfIsLastReplica
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Determine whether any DSAs other than that with DN pszNtdsDsaDN hold
|
||
|
replicas of a particular NC.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hld (IN) - LDAP handle to execute search with.
|
||
|
|
||
|
pszConfigNC (IN) - DN of the config NC. Used as a base for the search.
|
||
|
|
||
|
pszNC (IN) - NC for which to check for other replicas.
|
||
|
|
||
|
pszNtdsDsaDN (IN) - DN of the DSA object known to currently have a replica
|
||
|
of the NC. We are specifically looking for replicas *other than* this
|
||
|
one.
|
||
|
|
||
|
pfIsLastReplica (OUT) - On successful return, TRUE iff no DSAs hold replicas
|
||
|
of pszNC other than that with DN pszNtdsDsaDN.
|
||
|
|
||
|
Return Values:
|
||
|
|
||
|
Win error.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
LOG_FUNCTION2(IsLastReplicaOfNC, pszNC ? pszNC : L"(null)");
|
||
|
ASSERT(hld);
|
||
|
ASSERT(pszConfigNC);
|
||
|
ASSERT(pszNC);
|
||
|
ASSERT(pszNtdsDsaDN);
|
||
|
ASSERT(pfIsLastReplica);
|
||
|
|
||
|
if (
|
||
|
!hld
|
||
|
|| !pszConfigNC
|
||
|
|| !pszNC
|
||
|
|| !pszNtdsDsaDN
|
||
|
|| !pfIsLastReplica)
|
||
|
{
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
// Just checking for existence -- don't really want any attributes
|
||
|
// returned.
|
||
|
|
||
|
static LPWSTR rgpszDsaAttrsToRead[] = {
|
||
|
L"__invalid_attribute_name__",
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static WCHAR szFilterFormat[]
|
||
|
= L"(&(objectCategory=ntdsDsa)(hasMasterNCs=%ls)(!(distinguishedName=%ls)))";
|
||
|
|
||
|
*pfIsLastReplica = TRUE;
|
||
|
|
||
|
int ldStatus = 0;
|
||
|
DWORD err = 0;
|
||
|
LDAPMessage * pDsaResults = NULL;
|
||
|
LDAPMessage * pDsaEntry = NULL;
|
||
|
size_t cchFilter;
|
||
|
PWSTR pszFilter;
|
||
|
LDAP_TIMEVAL lTimeout = {3*60, 0}; // three minutes
|
||
|
|
||
|
do
|
||
|
{
|
||
|
cchFilter = sizeof(szFilterFormat) / sizeof(*szFilterFormat)
|
||
|
+ wcslen(pszNtdsDsaDN)
|
||
|
+ wcslen(pszNC);
|
||
|
|
||
|
pszFilter = (PWSTR) new BYTE[sizeof(WCHAR) * cchFilter];
|
||
|
|
||
|
swprintf(pszFilter, szFilterFormat, pszNC, pszNtdsDsaDN);
|
||
|
|
||
|
// Search config NC for any ntdsDsa object that hosts this NC other
|
||
|
// than that with dn pszNtdsDsaDN. Note that we cap the search at one
|
||
|
// returned object -- we're not really trying to enumerate, just
|
||
|
// checking for existence.
|
||
|
|
||
|
ldStatus = ldap_search_ext_sW(hld, pszConfigNC, LDAP_SCOPE_SUBTREE,
|
||
|
pszFilter, rgpszDsaAttrsToRead, 0,
|
||
|
NULL, NULL, &lTimeout, 1, &pDsaResults);
|
||
|
if (pDsaResults)
|
||
|
{
|
||
|
// Ignore any error (such as LDAP_SIZELIMIT_EXCEEDED) when the
|
||
|
// search returns results.
|
||
|
|
||
|
ldStatus = 0;
|
||
|
|
||
|
pDsaEntry = ldap_first_entry(hld, pDsaResults);
|
||
|
|
||
|
*pfIsLastReplica = (NULL == pDsaEntry);
|
||
|
} else if (ldStatus)
|
||
|
{
|
||
|
// Search failed and returned no results.
|
||
|
|
||
|
LOG_LDAP(L"Config NC search failed", ldStatus);
|
||
|
break;
|
||
|
} else
|
||
|
{
|
||
|
// No error, no results. This shouldn't happen.
|
||
|
|
||
|
LOG("ldap_search_ext_sW returned no results and no error!");
|
||
|
ASSERT(false);
|
||
|
}
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
if (NULL != pDsaResults) {
|
||
|
ldap_msgfree(pDsaResults);
|
||
|
}
|
||
|
|
||
|
if (pszFilter)
|
||
|
{
|
||
|
delete[] pszFilter;
|
||
|
}
|
||
|
|
||
|
if (!err && ldStatus) {
|
||
|
err = LdapMapErrorToWin32(ldStatus);
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// S_OK if this machine (the localhost) is the last replica of at least one
|
||
|
// non-domain NC, S_FALSE if not, or error otherwise. If S_OK, then the
|
||
|
// StringList will contain the DNs of the non domain NCs for which this
|
||
|
// machine is the last replica.
|
||
|
//
|
||
|
// based on code from jeffparh
|
||
|
//
|
||
|
// hld (IN) - LDAP handle bound to DSA to evaluate.
|
||
|
//
|
||
|
// result (OUT) - string list to receive DNs of the non-domain NCs.
|
||
|
|
||
|
HRESULT
|
||
|
IsLastNdncReplica(LDAP* hld, StringList& result)
|
||
|
{
|
||
|
LOG_FUNCTION(IsLastNdncReplica);
|
||
|
ASSERT(hld);
|
||
|
ASSERT(result.empty());
|
||
|
|
||
|
HRESULT hr = S_FALSE;
|
||
|
LDAPMessage* rootResults = 0;
|
||
|
PWSTR* configNc = 0;
|
||
|
PWSTR* schemaNc = 0;
|
||
|
PWSTR* domainNc = 0;
|
||
|
PWSTR* masterNcs = 0;
|
||
|
PWSTR* ntdsDsaDn = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Gather basic rootDSE info.
|
||
|
|
||
|
static PWSTR ROOT_ATTRS_TO_READ[] =
|
||
|
{
|
||
|
LDAP_OPATT_NAMING_CONTEXTS_W,
|
||
|
LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W,
|
||
|
LDAP_OPATT_CONFIG_NAMING_CONTEXT_W,
|
||
|
LDAP_OPATT_SCHEMA_NAMING_CONTEXT_W,
|
||
|
LDAP_OPATT_DS_SERVICE_NAME_W,
|
||
|
0
|
||
|
};
|
||
|
|
||
|
LOG(L"Calling ldap_search_s");
|
||
|
|
||
|
int ldStatus =
|
||
|
ldap_search_sW(
|
||
|
hld,
|
||
|
0,
|
||
|
LDAP_SCOPE_BASE,
|
||
|
L"(objectClass=*)",
|
||
|
ROOT_ATTRS_TO_READ,
|
||
|
0,
|
||
|
&rootResults);
|
||
|
if (ldStatus)
|
||
|
{
|
||
|
LOG_LDAP(L"RootDSE search failed", ldStatus);
|
||
|
hr = LdapToHresult(ldStatus);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
configNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_CONFIG_NAMING_CONTEXT_W);
|
||
|
schemaNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_SCHEMA_NAMING_CONTEXT_W);
|
||
|
domainNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W);
|
||
|
masterNcs = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_NAMING_CONTEXTS_W);
|
||
|
ntdsDsaDn = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_DS_SERVICE_NAME_W);
|
||
|
|
||
|
if (
|
||
|
(0 == configNc)
|
||
|
|| (0 == schemaNc)
|
||
|
|| (0 == domainNc)
|
||
|
|| (0 == masterNcs)
|
||
|
|| (0 == ntdsDsaDn))
|
||
|
{
|
||
|
LOG(L"Can't find key rootDSE attributes!");
|
||
|
|
||
|
hr = Win32ToHresult(ERROR_DS_UNAVAILABLE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// There is only one value for each of these attributes...
|
||
|
|
||
|
ASSERT(1 == ldap_count_valuesW(configNc));
|
||
|
ASSERT(1 == ldap_count_valuesW(schemaNc));
|
||
|
ASSERT(1 == ldap_count_valuesW(domainNc));
|
||
|
ASSERT(1 == ldap_count_valuesW(ntdsDsaDn));
|
||
|
|
||
|
DWORD masterNcCount = ldap_count_valuesW(masterNcs);
|
||
|
|
||
|
LOG(String::format(L"masterNcCount = %1!d!", masterNcCount));
|
||
|
|
||
|
// '3' => 1 nc for config, 1 nc for schema, 1 nc for this DC's own
|
||
|
// domain.
|
||
|
|
||
|
if (masterNcCount <= 3)
|
||
|
{
|
||
|
// DSA holds no master NCs other than config, schema, and its own
|
||
|
// domain. Thus, it is not the last replica of any NDNC.
|
||
|
|
||
|
LOG(L"This dsa holds no master NCs other than config, schema, and domain");
|
||
|
|
||
|
ASSERT(3 == masterNcCount);
|
||
|
ASSERT(0 == ldStatus);
|
||
|
ASSERT(hr == S_FALSE);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Loop through non-config/schema/domain NCs to determine those for
|
||
|
// which the DSA is the last replica.
|
||
|
|
||
|
for (int i = 0; 0 != masterNcs[i]; ++i)
|
||
|
{
|
||
|
PWSTR nc = masterNcs[i];
|
||
|
|
||
|
LOG(L"Evaluating " + String(nc));
|
||
|
|
||
|
if (
|
||
|
(0 != wcscmp(nc, *configNc))
|
||
|
&& (0 != wcscmp(nc, *schemaNc))
|
||
|
&& (0 != wcscmp(nc, *domainNc)))
|
||
|
{
|
||
|
// A non-config/schema/domain NC.
|
||
|
|
||
|
LOG(L"Calling IsLastReplicaOfNC on " + String(nc));
|
||
|
|
||
|
BOOL isLastReplica = FALSE;
|
||
|
DWORD err =
|
||
|
IsLastReplicaOfNC(
|
||
|
hld,
|
||
|
*configNc,
|
||
|
nc,
|
||
|
*ntdsDsaDn,
|
||
|
&isLastReplica);
|
||
|
if (err)
|
||
|
{
|
||
|
LOG(L"IsLastReplicaOfNC() failed");
|
||
|
|
||
|
hr = Win32ToHresult(err);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (isLastReplica)
|
||
|
{
|
||
|
// This DSA is indeed the last replica of this particular
|
||
|
// NC. Return the DN of this NC to our caller.
|
||
|
|
||
|
LOG(L"last replica of " + String(nc));
|
||
|
|
||
|
result.push_back(nc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LOG(L"not last replica of " + String(nc));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we broke out of the prior loop with an error, jump out to the
|
||
|
// cleanup section.
|
||
|
|
||
|
BREAK_ON_FAILED_HRESULT(hr);
|
||
|
|
||
|
hr = result.size() > 0 ? S_OK : S_FALSE;
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
if (rootResults)
|
||
|
{
|
||
|
ldap_msgfree(rootResults);
|
||
|
}
|
||
|
|
||
|
if (0 != configNc)
|
||
|
{
|
||
|
ldap_value_freeW(configNc);
|
||
|
}
|
||
|
|
||
|
if (0 != schemaNc)
|
||
|
{
|
||
|
ldap_value_freeW(schemaNc);
|
||
|
}
|
||
|
|
||
|
if (0 != domainNc)
|
||
|
{
|
||
|
ldap_value_freeW(domainNc);
|
||
|
}
|
||
|
|
||
|
if (0 != masterNcs)
|
||
|
{
|
||
|
ldap_value_freeW(masterNcs);
|
||
|
}
|
||
|
|
||
|
if (0 != ntdsDsaDn)
|
||
|
{
|
||
|
ldap_value_freeW(ntdsDsaDn);
|
||
|
}
|
||
|
|
||
|
#ifdef LOGGING_BUILD
|
||
|
LOG_HRESULT(hr);
|
||
|
|
||
|
for (
|
||
|
StringList::iterator i = result.begin();
|
||
|
i != result.end();
|
||
|
++i)
|
||
|
{
|
||
|
LOG(*i);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// S_OK if this machine (the localhost) is the last replica of at least one
|
||
|
// non-domain NC, S_FALSE if not, or error otherwise.
|
||
|
//
|
||
|
// result - If S_OK is returned, receives the DNs of the non domain NCs for
|
||
|
// which this machine is the last replica. Should be empty on entry.
|
||
|
|
||
|
HRESULT
|
||
|
IsLastNonDomainNamingContextReplica(StringList& result)
|
||
|
{
|
||
|
LOG_FUNCTION(IsLastNonDomainNamingContextReplica);
|
||
|
ASSERT(result.empty());
|
||
|
|
||
|
result.clear();
|
||
|
|
||
|
HRESULT hr = S_FALSE;
|
||
|
LDAP* hld = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Connect to target DSA.
|
||
|
|
||
|
LOG(L"Calling ldap_open");
|
||
|
|
||
|
hld = ldap_openW(L"localhost", LDAP_PORT);
|
||
|
if (!hld)
|
||
|
{
|
||
|
LOG("Cannot open LDAP connection to localhost");
|
||
|
hr = Win32ToHresult(ERROR_DS_UNAVAILABLE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Bind using logged-in user's credentials.
|
||
|
|
||
|
int ldStatus = ldap_bind_s(hld, 0, 0, LDAP_AUTH_NEGOTIATE);
|
||
|
if (ldStatus)
|
||
|
{
|
||
|
LOG_LDAP(L"LDAP bind failed", ldStatus);
|
||
|
|
||
|
hr = LdapToHresult(ldStatus);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// go do the real work
|
||
|
|
||
|
hr = IsLastNdncReplica(hld, result);
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
if (hld)
|
||
|
{
|
||
|
ldap_unbind(hld);
|
||
|
}
|
||
|
|
||
|
LOG_HRESULT(hr);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static const DWORD HELP_MAP[] =
|
||
|
{
|
||
|
0, 0
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
NonDomainNcErrorDialog::NonDomainNcErrorDialog(StringList& ndncList_)
|
||
|
:
|
||
|
Dialog(IDD_NON_DOMAIN_NC_ERROR, HELP_MAP),
|
||
|
ndncList(ndncList_),
|
||
|
warnIcon(0)
|
||
|
{
|
||
|
LOG_CTOR(NonDomainNcErrorDialog);
|
||
|
|
||
|
ASSERT(ndncList.size());
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NonDomainNcErrorDialog::~NonDomainNcErrorDialog()
|
||
|
{
|
||
|
LOG_DTOR(NonDomainNcErrorDialog);
|
||
|
|
||
|
if (warnIcon)
|
||
|
{
|
||
|
Win::DestroyIcon(warnIcon);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
NonDomainNcErrorDialog::OnInit()
|
||
|
{
|
||
|
LOG_FUNCTION(NonDomainNcErrorDialog::OnInit);
|
||
|
|
||
|
// set the warning icon NTRAID#NTBUG9-239678-2000/11/28-sburns
|
||
|
|
||
|
HRESULT hr = Win::LoadImage(IDI_WARN, warnIcon);
|
||
|
ASSERT(SUCCEEDED(hr));
|
||
|
|
||
|
Win::SendMessage(
|
||
|
Win::GetDlgItem(hwnd, IDC_WARNING_ICON),
|
||
|
STM_SETICON,
|
||
|
reinterpret_cast<WPARAM>(warnIcon),
|
||
|
0);
|
||
|
|
||
|
PopulateListView();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Unwraps the safearray of variants inside a variant, extracts the strings
|
||
|
// inside them, and catenates the strings together with semicolons in between.
|
||
|
// Return empty string on error.
|
||
|
//
|
||
|
// variant - in, the variant containing a safearray of variants of bstr.
|
||
|
|
||
|
String
|
||
|
GetNdncDescriptionHelper(VARIANT* variant)
|
||
|
{
|
||
|
LOG_FUNCTION(GetNdncDescriptionHelper);
|
||
|
ASSERT(variant);
|
||
|
ASSERT(V_VT(variant) == (VT_ARRAY | VT_VARIANT));
|
||
|
|
||
|
String result;
|
||
|
|
||
|
SAFEARRAY* psa = V_ARRAY(variant);
|
||
|
|
||
|
do
|
||
|
{
|
||
|
ASSERT(psa);
|
||
|
ASSERT(psa != (SAFEARRAY*)-1);
|
||
|
|
||
|
if (!psa or psa == (SAFEARRAY*)-1)
|
||
|
{
|
||
|
LOG(L"variant not safe array");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (::SafeArrayGetDim(psa) != 1)
|
||
|
{
|
||
|
LOG(L"safe array: wrong number of dimensions");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
VARTYPE vt = VT_EMPTY;
|
||
|
HRESULT hr = ::SafeArrayGetVartype(psa, &vt);
|
||
|
if (FAILED(hr) || vt != VT_VARIANT)
|
||
|
{
|
||
|
LOG(L"safe array: wrong element type");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
long lower = 0;
|
||
|
long upper = 0;
|
||
|
|
||
|
hr = ::SafeArrayGetLBound(psa, 1, &lower);
|
||
|
BREAK_ON_FAILED_HRESULT2(hr, L"can't get lower bound");
|
||
|
|
||
|
hr = ::SafeArrayGetUBound(psa, 1, &upper);
|
||
|
BREAK_ON_FAILED_HRESULT2(hr, L"can't get upper bound");
|
||
|
|
||
|
VARIANT varItem;
|
||
|
::VariantInit(&varItem);
|
||
|
|
||
|
for (long i = lower; i <= upper; ++i)
|
||
|
{
|
||
|
hr = ::SafeArrayGetElement(psa, &i, &varItem);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
LOG(String::format(L"index %1!d! failed", i));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
result += V_BSTR(&varItem);
|
||
|
|
||
|
if (i < upper)
|
||
|
{
|
||
|
result += L";";
|
||
|
}
|
||
|
|
||
|
::VariantClear(&varItem);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
LOG(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// bind to an ndnc, read it's description(s), and return them catenated
|
||
|
// together. Return empty string on error.
|
||
|
//
|
||
|
// ndncDn - in, DN of the ndnc
|
||
|
|
||
|
String
|
||
|
GetNdncDescription(const String& ndncDn)
|
||
|
{
|
||
|
LOG_FUNCTION2(GetNdncDescription, ndncDn);
|
||
|
ASSERT(!ndncDn.empty());
|
||
|
|
||
|
String result;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
String path = L"LDAP://" + ndncDn;
|
||
|
|
||
|
SmartInterface<IADs> iads(0);
|
||
|
IADs* dumb = 0;
|
||
|
|
||
|
HRESULT hr =
|
||
|
::ADsGetObject(
|
||
|
path.c_str(),
|
||
|
__uuidof(iads),
|
||
|
reinterpret_cast<void**>(&dumb));
|
||
|
BREAK_ON_FAILED_HRESULT2(hr, L"ADsGetObject failed on " + path);
|
||
|
|
||
|
iads.Acquire(dumb);
|
||
|
|
||
|
// description is a multivalued attrbute for no apparent good reason.
|
||
|
// so we need to walk an array of values.
|
||
|
|
||
|
_variant_t variant;
|
||
|
hr = iads->GetEx(AutoBstr(L"description"), &variant);
|
||
|
BREAK_ON_FAILED_HRESULT2(hr, L"read description failed");
|
||
|
|
||
|
result = GetNdncDescriptionHelper(&variant);
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
LOG(result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Build a list view with two columns, one for the DN of the ndncs for which
|
||
|
// this box is the last replica, another for the description(s) of those
|
||
|
// ndncs.
|
||
|
|
||
|
void
|
||
|
NonDomainNcErrorDialog::PopulateListView()
|
||
|
{
|
||
|
LOG_FUNCTION(NonDomainNcErrorDialog::PopulateListView);
|
||
|
|
||
|
HWND view = Win::GetDlgItem(hwnd, IDC_NDNC_LIST);
|
||
|
|
||
|
// add a column to the list view for the DN
|
||
|
|
||
|
LVCOLUMN column;
|
||
|
::ZeroMemory(&column, sizeof column);
|
||
|
|
||
|
column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
||
|
column.fmt = LVCFMT_LEFT;
|
||
|
|
||
|
int width = 0;
|
||
|
String::load(IDS_NDNC_LIST_NAME_COLUMN_WIDTH).convert(width);
|
||
|
column.cx = width;
|
||
|
|
||
|
String label = String::load(IDS_NDNC_LIST_NAME_COLUMN);
|
||
|
column.pszText = const_cast<wchar_t*>(label.c_str());
|
||
|
|
||
|
Win::ListView_InsertColumn(view, 0, column);
|
||
|
|
||
|
// add a column to the list view for description.
|
||
|
|
||
|
String::load(IDS_NDNC_LIST_DESC_COLUMN_WIDTH).convert(width);
|
||
|
column.cx = width;
|
||
|
label = String::load(IDS_NDNC_LIST_DESC_COLUMN);
|
||
|
column.pszText = const_cast<wchar_t*>(label.c_str());
|
||
|
|
||
|
Win::ListView_InsertColumn(view, 1, column);
|
||
|
|
||
|
// Load up the edit box with the DNs we aliased in the ctor.
|
||
|
|
||
|
LVITEM item;
|
||
|
::ZeroMemory(&item, sizeof item);
|
||
|
|
||
|
for (
|
||
|
StringList::iterator i = ndncList.begin();
|
||
|
i != ndncList.end();
|
||
|
++i)
|
||
|
{
|
||
|
item.mask = LVIF_TEXT;
|
||
|
item.pszText = const_cast<wchar_t*>(i->c_str());
|
||
|
item.iSubItem = 0;
|
||
|
|
||
|
item.iItem = Win::ListView_InsertItem(view, item);
|
||
|
|
||
|
// add the description sub-item to the list control
|
||
|
|
||
|
String description = GetNdncDescription(*i);
|
||
|
|
||
|
item.mask = LVIF_TEXT;
|
||
|
item.pszText = const_cast<wchar_t*>(description.c_str());
|
||
|
item.iSubItem = 1;
|
||
|
|
||
|
Win::ListView_SetItem(view, item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool
|
||
|
NonDomainNcErrorDialog::OnCommand(
|
||
|
HWND /* windowFrom */ ,
|
||
|
unsigned controlIDFrom,
|
||
|
unsigned code)
|
||
|
{
|
||
|
// LOG_FUNCTION(NonDomainNcErrorDialog::OnCommand);
|
||
|
|
||
|
if (code == BN_CLICKED)
|
||
|
{
|
||
|
switch (controlIDFrom)
|
||
|
{
|
||
|
case IDOK:
|
||
|
case IDCANCEL:
|
||
|
{
|
||
|
Win::EndDialog(hwnd, controlIDFrom);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// NTRAID#NTBUG9-239678-2000/11/28-sburns
|
||
|
|
||
|
case IDC_SHOW_HELP:
|
||
|
{
|
||
|
if (code == BN_CLICKED)
|
||
|
{
|
||
|
Win::HtmlHelp(
|
||
|
hwnd,
|
||
|
L"adconcepts.chm::/ADHelpDemoteWithNDNC.htm",
|
||
|
HH_DISPLAY_TOPIC,
|
||
|
0);
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
// do nothing
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
bool
|
||
|
IsLastReplicaOfNonDomainNamingContexts()
|
||
|
{
|
||
|
LOG_FUNCTION(IsLastReplicaOfNonDomainNamingContexts);
|
||
|
|
||
|
bool result = false;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
State::RunContext context = State::GetInstance().GetRunContext();
|
||
|
if (context != State::NT5_DC)
|
||
|
{
|
||
|
// not a DC, so can't be replica of any NCs
|
||
|
|
||
|
LOG(L"not a DC");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Find the list of non-domain NCs that this DC is the last replica
|
||
|
// (if any). If we find some, gripe at the user.
|
||
|
|
||
|
StringList ndncList;
|
||
|
HRESULT hr = IsLastNonDomainNamingContextReplica(ndncList);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
popup.Error(
|
||
|
Win::GetDesktopWindow(),
|
||
|
hr,
|
||
|
IDS_FAILED_TO_READ_NDNC_INFO);
|
||
|
|
||
|
result = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
LOG(L"Not last replica of non-domain NCs");
|
||
|
ASSERT(result == false);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
result = true;
|
||
|
|
||
|
// there should be at least one DN in the list.
|
||
|
|
||
|
ASSERT(ndncList.size());
|
||
|
|
||
|
NonDomainNcErrorDialog(ndncList).ModalExecute(Win::GetDesktopWindow());
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
LOG(result ? L"true" : L"false");
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|