windows-nt/Source/XPSP1/NT/admin/dsutils/displayspecifierupgrade/previoussource/analyst.cpp
2020-09-26 16:20:57 +08:00

548 lines
12 KiB
C++

// Active Directory Display Specifier Upgrade Tool
//
// Copyright (c) 2001 Microsoft Corporation
//
// class Analyst: analyzes the display specifiers, logs the findings, and
// compiles a set of corrective actions.
//
// 9 Mar 2001 sburns
#include "headers.hxx"
#include "resource.h"
#include "AdsiHelpers.hpp"
#include "Analyst.hpp"
#include "Amanuensis.hpp"
#include "Repairer.hpp"
#include "ChangedObjectHandlerList.hpp"
#include "ChangedObjectHandler.hpp"
Analyst::Analyst(
const String& targetDomainControllerName,
Amanuensis& amanuensis_,
Repairer& repairer_)
:
targetDcName(targetDomainControllerName),
ldapPrefix(),
rootDse(0),
// alias the objects
amanuensis(amanuensis_),
repairer(repairer_)
{
LOG_CTOR(Analyst);
ASSERT(!targetDcName.empty());
}
// basic idea: if the error is critical and analysis should not continue, set
// hr to a failure value, and break out, propagating the error backward. If
// the error is non-critical and analysis should continue, log the error, skip
// the current operation, and set hr to S_FALSE.
HRESULT
AssessErrorSeverity(HRESULT hrIn)
{
HRESULT hr = hrIn;
if (SUCCEEDED(hr))
{
return hr;
}
switch (hr)
{
case 0:
{
}
// CODEWORK: we need to define what errors are critical...
default:
{
// do nothing
break;
}
}
return hr;
}
HRESULT
Analyst::AnalyzeDisplaySpecifiers()
{
LOG_FUNCTION(Analyst::AnalyzeDisplaySpecifiers);
HRESULT hr = S_OK;
do
{
Computer targetDc(targetDcName);
hr = targetDc.Refresh();
if (FAILED(hr))
{
amanuensis.AddErrorEntry(
hr,
String::format(
IDS_CANT_TARGET_MACHINE,
targetDcName.c_str()));
break;
}
if (!targetDc.IsDomainController())
{
amanuensis.AddEntry(
String::format(
IDS_TARGET_IS_NOT_DC,
targetDcName.c_str()));
break;
}
String dcName = targetDc.GetActivePhysicalFullDnsName();
ldapPrefix = L"LDAP://" + dcName + L"/";
//
// Find the DN of the configuration container.
//
// Bind to the rootDSE object. We will keep this binding handle
// open for the duration of the analysis and repair phases in order
// to keep a server session open. If we decide to pass creds to the
// AdsiOpenObject call in a later revision, then by keeping the
// session open we will not need to pass the password to subsequent
// AdsiOpenObject calls.
hr = AdsiOpenObject<IADs>(ldapPrefix + L"RootDSE", rootDse);
if (FAILED(hr))
{
amanuensis.AddErrorEntry(
hr,
String::format(
IDS_UNABLE_TO_CONNECT_TO_DC,
dcName.c_str()));
break;
}
// read the configuration naming context.
_variant_t variant;
hr =
rootDse->Get(
AutoBstr(LDAP_OPATT_CONFIG_NAMING_CONTEXT_W),
&variant);
if (FAILED(hr))
{
LOG(L"can't read config NC");
amanuensis.AddErrorEntry(
hr,
IDS_UNABLE_TO_READ_DIRECTORY_INFO);
break;
}
String configNc = V_BSTR(&variant);
LOG(configNc);
ASSERT(!configNc.empty());
//
// Here we go...
//
hr = AnalyzeDisplaySpecifierContainers(configNc);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
LOG_HRESULT(hr);
return hr;
}
HRESULT
Analyst::AnalyzeDisplaySpecifierContainers(const String& configurationDn)
{
LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierContainers, configurationDn);
ASSERT(!configurationDn.empty());
HRESULT hr = S_OK;
static const int LOCALEIDS[] =
{
// a list of all the non-english locale IDs that we support
0x401,
0x404,
0x405,
0x406,
0x407,
0x408,
0x40b,
0x40c,
0x40d,
0x40e,
0x410,
0x411,
0x412,
0x413,
0x414,
0x415,
0x416,
0x419,
0x41d,
0x41f,
0x804,
0x816,
0xc0a,
0
};
// compose the LDAP path of the display specifiers container
String rootContainerDn = L"CN=DisplaySpecifiers," + configurationDn;
for (
int i = 0;
(i < sizeof(LOCALEIDS) / sizeof(int))
&& LOCALEIDS[i];
++i)
{
hr = AnalyzeDisplaySpecifierContainer(LOCALEIDS[i], rootContainerDn);
BREAK_ON_FAILED_HRESULT(hr);
}
LOG_HRESULT(hr);
return hr;
}
HRESULT
Analyst::AnalyzeDisplaySpecifierContainer(
int localeId,
const String& rootContainerDn)
{
LOG_FUNCTION2(
Analyst::AnalyzeDisplaySpecifierContainer,
rootContainerDn);
ASSERT(!rootContainerDn.empty());
ASSERT(localeId);
HRESULT hr = S_OK;
do
{
String childContainerDn =
ldapPrefix
+ String::format(L"CN=%1!3x!,", localeId) + rootContainerDn;
// Attempt to bind to the container.
SmartInterface<IADs> iads(0);
hr = AdsiOpenObject<IADs>(childContainerDn, iads);
if (hr == E_ADS_UNKNOWN_OBJECT)
{
// The container object does not exist. This is possible because
// the user has manually removed the container, or because it
// was never created due to an aboted post-dcpromo import of the
// display specifiers when the forest root dc was first promoted.
repairer.AddCreateContainerWorkItem(localeId);
hr = S_OK;
break;
}
BREAK_ON_FAILED_HRESULT(hr);
// At this point, the bind succeeded, so the child container exists.
// So now we want to examine objects in that container.
hr =
AnalyzeDisplaySpecifierObjects(
localeId,
childContainerDn);
}
while (0);
LOG_HRESULT(hr);
hr = AssessErrorSeverity(hr);
return hr;
}
HRESULT
Analyst::AnalyzeDisplaySpecifierObjects(
int localeId,
const String& containerDn)
{
LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierObjects, containerDn);
ASSERT(localeId);
ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do
{
// Part 1: deal with new objects added in Whistler
hr = AnalyzeAddedObjects(localeId, containerDn);
hr = AssessErrorSeverity(hr);
BREAK_ON_FAILED_HRESULT(hr);
// Part 2: deal with objects that have changed from Win2k to Whistler
hr = AnalyzeChangedObjects(localeId, containerDn);
hr = AssessErrorSeverity(hr);
BREAK_ON_FAILED_HRESULT(hr);
// Part 3: deal with objects that have been deleted in whistler
// This part is easy: there are no deletions.
}
while (0);
LOG_HRESULT(hr);
return hr;
}
bool
RepairWasRunPreviously()
{
LOG_FUNCTION(RepairWasRunPreviously);
bool result = false;
// CODEWORK: need to complete
LOG_BOOL(result);
return result;
}
HRESULT
Analyst::AnalyzeAddedObjects(
int localeId,
const String& containerDn)
{
LOG_FUNCTION2(Analyst::AnalyzeAddedObjects, containerDn);
ASSERT(localeId);
ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do
{
static const String ADDED_OBJECTS[] =
{
L"msMQ-Custom-Recipient-Display",
L"msMQ-Group-Display",
L"msCOM-PartitionSet-Display",
L"msCOM-Partition-Display",
L"lostAndFound-Display",
L"inetOrgPerson-Display",
L"",
};
for (
int i = 0;
i < (sizeof(ADDED_OBJECTS) / sizeof(String))
&& !ADDED_OBJECTS[i].empty();
++i)
{
String objectName = ADDED_OBJECTS[i];
String objectPath =
ldapPrefix + L"CN=" + objectName + L"," + containerDn;
SmartInterface<IADs> iads(0);
hr = AdsiOpenObject<IADs>(objectPath, iads);
if (hr == E_ADS_UNKNOWN_OBJECT)
{
// The object does not exist. This is what we expect. We want
// to add the object in the repair phase.
repairer.AddCreateObjectWorkItem(localeId, objectName);
hr = S_OK;
continue;
}
else if (SUCCEEDED(hr))
{
// The object already exists. Well, that's not expected, unless
// we've already run the tool.
if (!RepairWasRunPreviously())
{
// we didn't create the object. If the user did, they did
// it manually, and we don't support that.
// cause the existing object to be deleted
repairer.AddDeleteObjectWorkItem(localeId, objectName);
// cause a new, replacement object to be created.
repairer.AddCreateObjectWorkItem(localeId, objectName);
hr = S_OK;
continue;
}
}
else
{
ASSERT(FAILED(hr));
LOG(L"Unexpected error attempting to bind to " + objectName);
amanuensis.AddErrorEntry(
hr,
String::format(
IDS_ERROR_BINDING_TO_OBJECT,
objectName.c_str(),
objectPath.c_str()));
// move on to the next object
hr = S_FALSE;
continue;
}
}
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
LOG_HRESULT(hr);
return hr;
}
HRESULT
Analyst::AnalyzeChangedObjects(
int localeId,
const String& containerDn)
{
LOG_FUNCTION2(Analyst::AnalyzeChangedObjects, containerDn);
ASSERT(localeId);
ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
static const ChangedObjectHandlerList handlers;
for (
ChangedObjectHandlerList::iterator i = handlers.begin();
i != handlers.end();
++i)
{
hr = AnalyzeChangedObject(localeId, containerDn, **i);
hr = AssessErrorSeverity(hr);
BREAK_ON_FAILED_HRESULT(hr);
}
LOG_HRESULT(hr);
return hr;
}
HRESULT
Analyst::AnalyzeChangedObject(
int localeId,
const String& containerDn,
const ChangedObjectHandler& changeHandler)
{
LOG_FUNCTION2(Analyst::AnalyzeChangedObject, changeHandler.GetObjectName());
ASSERT(localeId);
ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do
{
String objectName = changeHandler.GetObjectName();
String objectPath =
ldapPrefix + L"CN=" + objectName + L"," + containerDn;
SmartInterface<IADs> iads(0);
hr = AdsiOpenObject<IADs>(objectPath, iads);
if (hr == E_ADS_UNKNOWN_OBJECT)
{
// The object does not exist. This is possible because the user has
// manually removed the container, or because it was never created
// due to an aboted post-dcpromo import of the display specifiers
// when the forest root dc was first promoted.
// Add a work item to create the missing object
repairer.AddCreateObjectWorkItem(localeId, objectName);
hr = S_OK;
break;
}
if (FAILED(hr))
{
// any other error is quittin' time.
break;
}
// At this point, the display specifier object exists. Determine if
// if has been touched since its creation.
// Compare usnCreated to usnChanged
_variant_t variant;
hr = iads->Get(AutoBstr(L"usnCreated"), &variant);
if (FAILED(hr))
{
LOG(L"Error reading usnCreated");
break;
}
// CODEWORK: need to complete this
hr = changeHandler.HandleChange(
localeId,
containerDn,
iads,
amanuensis,
repairer);
}
while (0);
LOG_HRESULT(hr);
return hr;
}