You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1943 lines
44 KiB
1943 lines
44 KiB
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
//
|
|
// code common to several pages
|
|
//
|
|
// 12-16-97 sburns
|
|
|
|
|
|
|
|
#include "headers.hxx"
|
|
#include "common.hpp"
|
|
#include "resource.h"
|
|
#include "state.hpp"
|
|
#include "ds.hpp"
|
|
#include <DiagnoseDcNotFound.hpp>
|
|
#include <ValidateDomainName.h>
|
|
|
|
|
|
// Creates the fonts for setLargeFonts().
|
|
//
|
|
// hDialog - handle to a dialog to be used to retrieve a device
|
|
// context.
|
|
//
|
|
// bigBoldFont - receives the handle of the big bold font created.
|
|
|
|
void
|
|
InitFonts(
|
|
HWND hDialog,
|
|
HFONT& bigBoldFont)
|
|
{
|
|
ASSERT(Win::IsWindow(hDialog));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
NONCLIENTMETRICS ncm;
|
|
|
|
// REVIEWED-2002/02/22-sburns call correctly passes byte count.
|
|
|
|
::ZeroMemory(&ncm, sizeof ncm);
|
|
|
|
ncm.cbSize = sizeof ncm;
|
|
|
|
// ISSUE-2002/02/27-sburns Seems to me that the second param here needs
|
|
// to be sizeof ncm
|
|
|
|
hr = Win::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
LOGFONT bigBoldLogFont = ncm.lfMessageFont;
|
|
bigBoldLogFont.lfWeight = FW_BOLD;
|
|
|
|
String fontName = String::load(IDS_BIG_BOLD_FONT_NAME);
|
|
|
|
// ensure null termination 260237
|
|
|
|
// REVIEWED-2002/02/22-sburns call correctly passes byte count.
|
|
|
|
::ZeroMemory(bigBoldLogFont.lfFaceName, LF_FACESIZE * sizeof WCHAR);
|
|
|
|
size_t fnLen = fontName.length();
|
|
fontName.copy(
|
|
bigBoldLogFont.lfFaceName,
|
|
|
|
// don't copy over the last null
|
|
|
|
min(LF_FACESIZE - 1, fnLen));
|
|
|
|
unsigned fontSize = 0;
|
|
String::load(IDS_BIG_BOLD_FONT_SIZE).convert(fontSize);
|
|
ASSERT(fontSize);
|
|
|
|
HDC hdc = 0;
|
|
hr = Win::GetDC(hDialog, hdc);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
bigBoldLogFont.lfHeight =
|
|
- ::MulDiv(
|
|
static_cast<int>(fontSize),
|
|
Win::GetDeviceCaps(hdc, LOGPIXELSY),
|
|
72);
|
|
|
|
hr = Win::CreateFontIndirect(bigBoldLogFont, bigBoldFont);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
Win::ReleaseDC(hDialog, hdc);
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
SetControlFont(HWND parentDialog, int controlID, HFONT font)
|
|
{
|
|
ASSERT(Win::IsWindow(parentDialog));
|
|
ASSERT(controlID);
|
|
ASSERT(font);
|
|
|
|
HWND control = Win::GetDlgItem(parentDialog, controlID);
|
|
|
|
if (control)
|
|
{
|
|
Win::SetWindowFont(control, font, true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
SetLargeFont(HWND dialog, int bigBoldResID)
|
|
{
|
|
ASSERT(Win::IsWindow(dialog));
|
|
ASSERT(bigBoldResID);
|
|
|
|
static HFONT bigBoldFont = 0;
|
|
if (!bigBoldFont)
|
|
{
|
|
InitFonts(dialog, bigBoldFont);
|
|
}
|
|
|
|
SetControlFont(dialog, bigBoldResID, bigBoldFont);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetDcName(const String& domainName, String& resultDcName)
|
|
{
|
|
LOG_FUNCTION(GetDcName);
|
|
ASSERT(!domainName.empty());
|
|
|
|
resultDcName.erase();
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
DOMAIN_CONTROLLER_INFO* info = 0;
|
|
hr =
|
|
MyDsGetDcName(
|
|
0,
|
|
domainName,
|
|
|
|
// pass the force rediscovery flag to make sure we don't pick up
|
|
// a dc that is down 262221
|
|
|
|
DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
|
|
info);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
ASSERT(info->DomainControllerName);
|
|
|
|
if (info->DomainControllerName)
|
|
{
|
|
// we found an NT5 domain
|
|
|
|
resultDcName =
|
|
Computer::RemoveLeadingBackslashes(info->DomainControllerName);
|
|
|
|
LOG(resultDcName);
|
|
}
|
|
|
|
::NetApiBufferFree(info);
|
|
|
|
if (resultDcName.empty())
|
|
{
|
|
hr = Win32ToHresult(ERROR_DOMAIN_CONTROLLER_NOT_FOUND);
|
|
}
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
BrowserSetComputer(const SmartInterface<IDsBrowseDomainTree>& browser)
|
|
{
|
|
LOG_FUNCTION(BrowserSetComputer);
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
State& state = State::GetInstance();
|
|
|
|
String username =
|
|
MassageUserName(
|
|
state.GetUserDomainName(),
|
|
state.GetUsername());
|
|
EncryptedString password = state.GetPassword();
|
|
|
|
String computer;
|
|
hr =
|
|
GetDcName(
|
|
state.GetUserDomainName(),
|
|
computer);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
LOG(L"Calling IDsBrowseDomainTree::SetComputer");
|
|
LOG(String::format(L"pszComputerName : %1", computer.c_str()));
|
|
LOG(String::format(L"pszUserName : %1", username.c_str()));
|
|
|
|
WCHAR* cleartext = password.GetClearTextCopy();
|
|
|
|
hr =
|
|
browser->SetComputer(
|
|
computer.c_str(),
|
|
username.c_str(),
|
|
cleartext);
|
|
|
|
password.DestroyClearTextCopy(cleartext);
|
|
|
|
LOG_HRESULT(hr);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
ReadDomainsHelper(bool bindWithCredentials, Callback* callback)
|
|
{
|
|
LOG_FUNCTION(ReadDomainsHelper);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
AutoCoInitialize coInit;
|
|
hr = coInit.Result();
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
SmartInterface<IDsBrowseDomainTree> browser;
|
|
hr = browser.AcquireViaCreateInstance(
|
|
CLSID_DsDomainTreeBrowser,
|
|
0,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDsBrowseDomainTree);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
if (bindWithCredentials)
|
|
{
|
|
LOG(L"binding with credentials");
|
|
|
|
hr = BrowserSetComputer(browser);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
|
|
LOG(L"Calling IDsBrowseDomainTree::GetDomains");
|
|
|
|
DOMAIN_TREE* tree = 0;
|
|
hr = browser->GetDomains(&tree, 0);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
ASSERT(tree);
|
|
|
|
if (tree && callback)
|
|
{
|
|
//lint -e534 ignore return value
|
|
callback->Execute(tree);
|
|
}
|
|
|
|
hr = browser->FreeDomains(&tree);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
String
|
|
BrowseForDomain(HWND parent)
|
|
{
|
|
LOG_FUNCTION(BrowseForDomain);
|
|
ASSERT(Win::IsWindow(parent));
|
|
|
|
String retval;
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
Win::WaitCursor cursor;
|
|
|
|
AutoCoInitialize coInit;
|
|
hr = coInit.Result();
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// CODEWORK: the credential page could cache an instance of the browser,
|
|
// rebuilding and setting the search root when only when new credentials
|
|
// are entered. Since the browser caches the last result, this would
|
|
// make subsequent retreivals of the domain much faster.
|
|
// addendum: tho the revised browser seems pretty quick
|
|
|
|
SmartInterface<IDsBrowseDomainTree> browser;
|
|
hr = browser.AcquireViaCreateInstance(
|
|
CLSID_DsDomainTreeBrowser,
|
|
0,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDsBrowseDomainTree);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = BrowserSetComputer(browser);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
PWSTR result = 0;
|
|
hr = browser->BrowseTo(parent, &result, 0);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
if (result)
|
|
{
|
|
retval = result;
|
|
::CoTaskMemFree(result);
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
popup.Error(parent, hr, IDS_CANT_BROWSE_FOREST);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
class RootDomainCollectorCallback : public Callback
|
|
{
|
|
public:
|
|
|
|
explicit
|
|
RootDomainCollectorCallback(StringList& domains_)
|
|
:
|
|
Callback(),
|
|
domains(domains_)
|
|
{
|
|
ASSERT(domains.empty());
|
|
domains.clear();
|
|
}
|
|
|
|
virtual
|
|
~RootDomainCollectorCallback()
|
|
{
|
|
}
|
|
|
|
virtual
|
|
int
|
|
Execute(void* param)
|
|
{
|
|
ASSERT(param);
|
|
|
|
// root domains are all those on the sibling link of the root
|
|
// node of the domain tree.
|
|
|
|
DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
|
|
if (tree)
|
|
{
|
|
for (
|
|
DOMAIN_DESC* desc = &(tree->aDomains[0]);
|
|
desc;
|
|
desc = desc->pdNextSibling)
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"pushing root domain %1",
|
|
desc->pszName));
|
|
|
|
domains.push_back(desc->pszName);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
StringList& domains;
|
|
};
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
ReadRootDomains(bool bindWithCredentials, StringList& domains)
|
|
{
|
|
LOG_FUNCTION(ReadRootDomains);
|
|
|
|
RootDomainCollectorCallback rdcc(domains);
|
|
|
|
return
|
|
ReadDomainsHelper(
|
|
bindWithCredentials,
|
|
&rdcc);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
IsRootDomain(bool bindWithCredentials)
|
|
{
|
|
LOG_FUNCTION(IsRootDomain);
|
|
|
|
static bool computed = false;
|
|
static bool isRoot = false;
|
|
|
|
if (!computed)
|
|
{
|
|
StringList domains;
|
|
if (SUCCEEDED(ReadRootDomains(bindWithCredentials, domains)))
|
|
{
|
|
String domain =
|
|
State::GetInstance().GetComputer().GetDomainDnsName();
|
|
for (
|
|
StringList::iterator i = domains.begin();
|
|
i != domains.end();
|
|
++i)
|
|
{
|
|
if (Dns::CompareNames((*i), domain) == DnsNameCompareEqual)
|
|
{
|
|
LOG(String::format(L"found match: %1", (*i).c_str()));
|
|
|
|
ASSERT(!(*i).empty());
|
|
|
|
isRoot = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
computed = true;
|
|
}
|
|
}
|
|
|
|
LOG(isRoot ? L"is root" : L"is not root");
|
|
|
|
return isRoot;
|
|
}
|
|
|
|
|
|
|
|
// first = child, second = parent
|
|
|
|
typedef std::pair<String, String> StringPair;
|
|
|
|
typedef
|
|
std::list<
|
|
StringPair,
|
|
Burnslib::Heap::Allocator<StringPair> >
|
|
ChildParentList;
|
|
|
|
class ChildDomainCollectorCallback : public Callback
|
|
{
|
|
public:
|
|
|
|
explicit
|
|
ChildDomainCollectorCallback(ChildParentList& domains_)
|
|
:
|
|
Callback(),
|
|
domains(domains_)
|
|
{
|
|
ASSERT(domains.empty());
|
|
domains.clear();
|
|
}
|
|
|
|
virtual
|
|
~ChildDomainCollectorCallback()
|
|
{
|
|
}
|
|
|
|
virtual
|
|
int
|
|
Execute(void* param)
|
|
{
|
|
LOG_FUNCTION(ChildDomainCollectorCallback::Execute);
|
|
|
|
ASSERT(param);
|
|
|
|
DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
|
|
if (tree)
|
|
{
|
|
typedef
|
|
std::deque<
|
|
DOMAIN_DESC*,
|
|
Burnslib::Heap::Allocator<DOMAIN_DESC*> >
|
|
DDDeque;
|
|
|
|
std::stack<DOMAIN_DESC*, DDDeque> s;
|
|
|
|
// first we push all the nodes for all root domains. These are
|
|
// the nodes on the sibling link of the tree root. Hereafter,
|
|
// the sibling link is only used to chase child domains.
|
|
|
|
for (
|
|
DOMAIN_DESC* desc = &(tree->aDomains[0]);
|
|
desc;
|
|
desc = desc->pdNextSibling)
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"pushing root domain %1",
|
|
desc->pszName));
|
|
|
|
s.push(desc);
|
|
}
|
|
|
|
// next, we work thru the stack, looking for nodes that have
|
|
// nodes on their child links. When we find such a node, we
|
|
// collect in the child list all the children on that link, and
|
|
// push them so that they will in turn be evaluated.
|
|
|
|
DWORD count = 0;
|
|
while (!s.empty())
|
|
{
|
|
DOMAIN_DESC* desc = s.top();
|
|
s.pop();
|
|
ASSERT(desc);
|
|
|
|
if (desc)
|
|
{
|
|
count++;
|
|
LOG(String::format(L"evaluating %1", desc->pszName));
|
|
|
|
String parentname = desc->pszName;
|
|
|
|
for (
|
|
DOMAIN_DESC* child = desc->pdChildList;
|
|
child;
|
|
child = child->pdNextSibling)
|
|
{
|
|
s.push(child);
|
|
|
|
String childname = child->pszName;
|
|
|
|
LOG(
|
|
String::format(
|
|
L"parent: %1 child: %2",
|
|
parentname.c_str(),
|
|
childname.c_str()));
|
|
|
|
domains.push_back(std::make_pair(childname, parentname));
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(count == tree->dwCount);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
ChildParentList& domains;
|
|
};
|
|
|
|
|
|
|
|
HRESULT
|
|
ReadChildDomains(bool bindWithCredentials, ChildParentList& domains)
|
|
{
|
|
LOG_FUNCTION(ReadChildDomains);
|
|
|
|
ChildDomainCollectorCallback cdcc(domains);
|
|
|
|
return
|
|
ReadDomainsHelper(
|
|
bindWithCredentials,
|
|
&cdcc);
|
|
}
|
|
|
|
|
|
|
|
String
|
|
GetParentDomainDnsName(
|
|
const String& childDomainDNSName,
|
|
bool bindWithCredentials)
|
|
{
|
|
LOG_FUNCTION2(GetParentDomainDnsName, childDomainDNSName);
|
|
ASSERT(!childDomainDNSName.empty());
|
|
|
|
ChildParentList domains;
|
|
|
|
if (SUCCEEDED(ReadChildDomains(bindWithCredentials, domains)))
|
|
{
|
|
for (
|
|
ChildParentList::iterator i = domains.begin();
|
|
i != domains.end();
|
|
++i)
|
|
{
|
|
if (
|
|
Dns::CompareNames(
|
|
(*i).first,
|
|
childDomainDNSName) == DnsNameCompareEqual)
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"found parent: %1",
|
|
(*i).second.c_str()));
|
|
|
|
ASSERT(!(*i).second.empty());
|
|
|
|
return (*i).second;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG(L"domain is not a child");
|
|
|
|
return String();
|
|
}
|
|
|
|
|
|
|
|
class DomainCollectorCallback : public Callback
|
|
{
|
|
public:
|
|
|
|
explicit
|
|
DomainCollectorCallback(StringList& domains_)
|
|
:
|
|
Callback(),
|
|
domains(domains_)
|
|
{
|
|
ASSERT(domains.empty());
|
|
domains.clear();
|
|
}
|
|
|
|
virtual
|
|
~DomainCollectorCallback()
|
|
{
|
|
}
|
|
|
|
virtual
|
|
int
|
|
Execute(void* param)
|
|
{
|
|
LOG_FUNCTION(DomainCollectorCallback::Execute);
|
|
|
|
ASSERT(param);
|
|
|
|
DOMAIN_TREE* tree = reinterpret_cast<DOMAIN_TREE*>(param);
|
|
if (tree)
|
|
{
|
|
for (DWORD i = 0; i < tree->dwCount; ++i)
|
|
{
|
|
PCWSTR name = tree->aDomains[i].pszName;
|
|
|
|
LOG(String::format(L"domain found: %1", name));
|
|
|
|
domains.push_back(name);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
StringList& domains;
|
|
};
|
|
|
|
|
|
|
|
HRESULT
|
|
ReadDomains(StringList& domains)
|
|
{
|
|
LOG_FUNCTION(ReadDomains);
|
|
|
|
DomainCollectorCallback dcc(domains);
|
|
|
|
return ReadDomainsHelper(true, &dcc);
|
|
}
|
|
|
|
|
|
|
|
String
|
|
BrowseForFolder(HWND parent, int titleResID)
|
|
{
|
|
LOG_FUNCTION(BrowseForFolder);
|
|
ASSERT(Win::IsWindow(parent));
|
|
ASSERT(titleResID > 0);
|
|
|
|
String result;
|
|
HRESULT hr = S_OK;
|
|
LPMALLOC pmalloc = 0;
|
|
LPITEMIDLIST drives = 0;
|
|
LPITEMIDLIST pidl = 0;
|
|
|
|
do
|
|
{
|
|
hr = Win::SHGetMalloc(pmalloc);
|
|
if (FAILED(hr) || !pmalloc)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// get a pidl for the local drives (really My Computer)
|
|
|
|
hr = Win::SHGetSpecialFolderLocation(parent, CSIDL_DRIVES, drives);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
BROWSEINFO info;
|
|
|
|
// REVIEWED-2002/02/25-sburns proper byte count is passed.
|
|
|
|
::ZeroMemory(&info, sizeof info);
|
|
|
|
String title = String::load(titleResID);
|
|
wchar_t buf[MAX_PATH + 1];
|
|
|
|
// REVIEWED-2002/02/25-sburns proper byte count is passed.
|
|
|
|
::ZeroMemory(buf, sizeof buf);
|
|
|
|
info.hwndOwner = parent;
|
|
info.pidlRoot = drives;
|
|
info.pszDisplayName = buf;
|
|
info.lpszTitle = title.c_str();
|
|
info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS;
|
|
|
|
// REVIEWED-2002/02/25-sburns pszDisplayName in info is at least
|
|
// MAX_PATH characters long.
|
|
|
|
pidl = Win::SHBrowseForFolder(info);
|
|
|
|
if (pidl)
|
|
{
|
|
result = Win::SHGetPathFromIDList(pidl);
|
|
}
|
|
|
|
}
|
|
while (0);
|
|
|
|
if (pmalloc)
|
|
{
|
|
pmalloc->Free(pidl);
|
|
pmalloc->Free(drives);
|
|
pmalloc->Release();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
CheckDriveType(const String& path)
|
|
{
|
|
LOG_FUNCTION(CheckDriveType);
|
|
ASSERT(!path.empty());
|
|
|
|
UINT type = Win::GetDriveType(path);
|
|
switch (type)
|
|
{
|
|
case DRIVE_FIXED:
|
|
{
|
|
return true;
|
|
}
|
|
case DRIVE_REMOVABLE:
|
|
{
|
|
// only legal iff volume = system volume
|
|
String vol = FS::GetRootFolder(path);
|
|
String sys = FS::GetRootFolder(Win::GetSystemDirectory());
|
|
if (vol.icompare(sys) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// all others bad
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
PathHasNonAsciiCharacters(const String& path, String& message)
|
|
{
|
|
LOG_FUNCTION2(PathHasNonAsciiCharacters, path);
|
|
ASSERT(!path.empty());
|
|
|
|
bool result = false;
|
|
|
|
// This set is all 7-bit ascii characters above ascii 31 that are
|
|
// not illegal FAT filename characters, plus : and \
|
|
|
|
static const String ALLOWED_ASCII(
|
|
L" !#$%&'()-."
|
|
L"0123456789"
|
|
L":@"
|
|
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
L"\\^_`"
|
|
L"abcdefghijklmnopqrstuvwxyz"
|
|
L"{}~" );
|
|
|
|
String illegalsFound;
|
|
String::size_type pos = 0;
|
|
|
|
while ((pos = path.find_first_not_of(ALLOWED_ASCII, pos)) != String::npos)
|
|
{
|
|
String illegal(1, path[pos]);
|
|
if (illegalsFound.find_first_of(illegal) == String::npos)
|
|
{
|
|
illegalsFound += illegal;
|
|
illegalsFound += L" ";
|
|
}
|
|
++pos;
|
|
}
|
|
|
|
if (!illegalsFound.empty())
|
|
{
|
|
message =
|
|
String::format(
|
|
IDS_NON_ASCII_PATH_CHARS,
|
|
path.c_str(),
|
|
illegalsFound.c_str());
|
|
result = true;
|
|
}
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateDcInstallPath(
|
|
const String& path,
|
|
HWND parent,
|
|
int editResId,
|
|
bool requiresNtfs5,
|
|
bool requiresAsciiCharacters,
|
|
bool requiresUncompressedFolder)
|
|
{
|
|
LOG_FUNCTION(ValidateDcInstallPath);
|
|
ASSERT(!path.empty());
|
|
ASSERT(editResId);
|
|
ASSERT(Win::IsWindow(parent));
|
|
|
|
bool result = false;
|
|
String message;
|
|
do
|
|
{
|
|
if (
|
|
(path.icompare(Win::GetWindowsDirectory()) == 0)
|
|
|| (path.icompare(Win::GetSystemWindowsDirectory()) == 0) )
|
|
{
|
|
message = String::format(IDS_PATH_IS_WINDIR, path.c_str());
|
|
break;
|
|
}
|
|
|
|
if (path.icompare(Win::GetSystemDirectory()) == 0)
|
|
{
|
|
message = String::format(IDS_PATH_IS_SYSTEM32, path.c_str());
|
|
break;
|
|
}
|
|
|
|
if (FS::GetPathSyntax(path) != FS::SYNTAX_ABSOLUTE_DRIVE)
|
|
{
|
|
message = String::format(IDS_BAD_PATH_FORMAT, path.c_str());
|
|
break;
|
|
}
|
|
|
|
if (requiresAsciiCharacters)
|
|
{
|
|
if (PathHasNonAsciiCharacters(path, message))
|
|
{
|
|
ASSERT(!message.empty());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!CheckDriveType(path))
|
|
{
|
|
message = String::format(IDS_BAD_DRIVE_TYPE, path.c_str());
|
|
break;
|
|
}
|
|
|
|
if (requiresNtfs5 && (FS::GetFileSystemType(path) != FS::NTFS5))
|
|
{
|
|
message = String::format(IDS_NOT_NTFS5, path.c_str());
|
|
break;
|
|
}
|
|
|
|
// prohibit paths that contain mounted volumes 325264
|
|
// even when they don't exist 435428
|
|
|
|
String mountRoot;
|
|
HRESULT hr = FS::GetVolumePathName(path, mountRoot);
|
|
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
// '3' == length of root of a "normal" volume ("C:\")
|
|
|
|
if (mountRoot.length() > 3)
|
|
{
|
|
message =
|
|
String::format(
|
|
IDS_PATH_CONTAINS_MOUNTED_VOLUMES,
|
|
path.c_str(),
|
|
mountRoot.c_str());
|
|
break;
|
|
}
|
|
|
|
DWORD attrs = 0;
|
|
hr = Win::GetFileAttributes(path, attrs);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// path exists
|
|
|
|
// reject paths that refer an existing file
|
|
|
|
if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
message = String::format(IDS_PATH_NOT_DIRECTORY, path.c_str());
|
|
break;
|
|
}
|
|
|
|
// if necessary, reject paths that are compressed
|
|
|
|
if ((attrs & FILE_ATTRIBUTE_COMPRESSED) && requiresUncompressedFolder)
|
|
{
|
|
message = String::format(IDS_PATH_IS_COMPRESSED, path.c_str());
|
|
break;
|
|
}
|
|
|
|
if (!FS::IsFolderEmpty(path))
|
|
{
|
|
if (
|
|
popup.MessageBox(
|
|
parent,
|
|
String::format(IDS_EMPTY_PATH, path.c_str()),
|
|
MB_ICONWARNING | MB_YESNO) == IDNO)
|
|
{
|
|
// don't gripe...silently disapprove
|
|
|
|
HWND edit = Win::GetDlgItem(parent, editResId);
|
|
Win::SendMessage(edit, EM_SETSEL, 0, -1);
|
|
Win::SetFocus(edit);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
while (0);
|
|
|
|
if (!message.empty())
|
|
{
|
|
popup.Gripe(parent, editResId, message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
DoLabelValidation(
|
|
HWND dialog,
|
|
int editResID,
|
|
int badSyntaxResID,
|
|
bool gripeOnNonRFC = true,
|
|
bool gripeOnNumericLabel = true)
|
|
{
|
|
LOG_FUNCTION(DoLabelValidation);
|
|
ASSERT(Win::IsWindow(dialog));
|
|
ASSERT(editResID > 0);
|
|
|
|
bool valid = false;
|
|
String label = Win::GetTrimmedDlgItemText(dialog, editResID);
|
|
switch (Dns::ValidateDnsLabelSyntax(label))
|
|
{
|
|
case Dns::NON_RFC:
|
|
{
|
|
if (gripeOnNonRFC)
|
|
{
|
|
// warn about non-rfc names
|
|
popup.Info(
|
|
dialog,
|
|
String::format(IDS_NON_RFC_NAME, label.c_str()));
|
|
}
|
|
|
|
// fall thru
|
|
}
|
|
case Dns::VALID:
|
|
{
|
|
valid = true;
|
|
break;
|
|
}
|
|
case Dns::TOO_LONG:
|
|
{
|
|
popup.Gripe(
|
|
dialog,
|
|
editResID,
|
|
String::format(
|
|
IDS_DNS_LABEL_TOO_LONG,
|
|
label.c_str(),
|
|
Dns::MAX_LABEL_LENGTH));
|
|
break;
|
|
}
|
|
case Dns::NUMERIC:
|
|
{
|
|
if (!gripeOnNumericLabel)
|
|
{
|
|
valid = true;
|
|
break;
|
|
}
|
|
|
|
// fall thru
|
|
}
|
|
case Dns::BAD_CHARS:
|
|
case Dns::INVALID:
|
|
default:
|
|
{
|
|
popup.Gripe(
|
|
dialog,
|
|
editResID,
|
|
String::format(badSyntaxResID, label.c_str()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateChildDomainLeafNameLabel(
|
|
HWND dialog,
|
|
int editResID,
|
|
bool gripeOnNonRfc)
|
|
{
|
|
LOG_FUNCTION(ValidateChildDomainLeafNameLabel);
|
|
|
|
String name = Win::GetTrimmedDlgItemText(dialog, editResID);
|
|
if (name.empty())
|
|
{
|
|
popup.Gripe(dialog, editResID, IDS_BLANK_LEAF_NAME);
|
|
return false;
|
|
}
|
|
|
|
// If parent is non-RFC, then so will be the child. The user has been
|
|
// griped to already, so don't gripe twice
|
|
// 291558
|
|
|
|
return
|
|
DoLabelValidation(
|
|
dialog,
|
|
editResID,
|
|
IDS_BAD_LABEL_SYNTAX,
|
|
gripeOnNonRfc,
|
|
|
|
// allow numeric labels. NTRAID#NTBUG9-321168-2001/02/20-sburns
|
|
|
|
false);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateSiteName(HWND dialog, int editResID)
|
|
{
|
|
LOG_FUNCTION(ValidateSiteName);
|
|
|
|
String name = Win::GetTrimmedDlgItemText(dialog, editResID);
|
|
if (name.empty())
|
|
{
|
|
popup.Gripe(dialog, editResID, IDS_BLANK_SITE_NAME);
|
|
return false;
|
|
}
|
|
|
|
// A site name is just a DNS label
|
|
|
|
return DoLabelValidation(dialog, editResID, IDS_BAD_SITE_SYNTAX);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ShowTroubleshooter(HWND parent, int topicResID)
|
|
{
|
|
LOG_FUNCTION(ShowTroubleshooter);
|
|
ASSERT(Win::IsWindow(parent));
|
|
|
|
String file = String::load(IDS_HTML_HELP_FILE);
|
|
String topic = String::load(topicResID);
|
|
ASSERT(!topic.empty());
|
|
|
|
LOG(String::format(L"file: %1 topic: %2", file.c_str(), topic.c_str()));
|
|
|
|
Win::HtmlHelp(
|
|
parent,
|
|
file,
|
|
HH_DISPLAY_TOPIC,
|
|
reinterpret_cast<DWORD_PTR>(topic.c_str()));
|
|
}
|
|
|
|
|
|
|
|
String
|
|
MassageUserName(const String& domainName, const String& userName)
|
|
{
|
|
LOG_FUNCTION2(MassageUserName, userName);
|
|
ASSERT(!userName.empty());
|
|
|
|
String result = userName;
|
|
|
|
do
|
|
{
|
|
if (userName.find(L"@") != String::npos)
|
|
{
|
|
// userName includes an @, looks like a UPN to us, so don't
|
|
// mess with it further 17699
|
|
|
|
LOG(L"looks like a UPN");
|
|
break;
|
|
}
|
|
|
|
if (!domainName.empty())
|
|
{
|
|
static const String DOMAIN_SEP_CHAR = L"\\";
|
|
String name = userName;
|
|
size_t pos = userName.find(DOMAIN_SEP_CHAR);
|
|
|
|
if (pos != String::npos)
|
|
{
|
|
// remove the domain name in the userName string and replace it
|
|
// with the domainName String
|
|
|
|
name = userName.substr(pos + 1);
|
|
ASSERT(!name.empty());
|
|
}
|
|
|
|
result = domainName + DOMAIN_SEP_CHAR + name;
|
|
break;
|
|
}
|
|
|
|
// otherwise, the username appears as "foo\bar", so we don't touch it.
|
|
}
|
|
while (0);
|
|
|
|
LOG(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
IsChildDomain(bool bindWithCredentials)
|
|
{
|
|
LOG_FUNCTION(IsChildDomain);
|
|
|
|
static bool computed = false;
|
|
static String parent;
|
|
|
|
if (!computed)
|
|
{
|
|
parent =
|
|
GetParentDomainDnsName(
|
|
State::GetInstance().GetComputer().GetDomainDnsName(),
|
|
bindWithCredentials);
|
|
computed = true;
|
|
}
|
|
|
|
LOG(
|
|
parent.empty()
|
|
? String(L"not a child")
|
|
: String::format(L"is child. parent: %1", parent.c_str()));
|
|
|
|
return !parent.empty();
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
IsForestRootDomain()
|
|
{
|
|
LOG_FUNCTION(IsForestRootDomain);
|
|
|
|
const Computer& c = State::GetInstance().GetComputer();
|
|
|
|
bool result = (c.GetDomainDnsName() == c.GetForestDnsName());
|
|
|
|
LOG(
|
|
String::format(
|
|
L"%1 a forest root domain",
|
|
result ? L"is" : L"is not"));
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateDomainExists(HWND dialog, int editResID, String& domainDnsName)
|
|
{
|
|
return ValidateDomainExists(dialog, String(), editResID, domainDnsName);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateDomainExists(
|
|
HWND dialog,
|
|
const String& domainName,
|
|
int editResId,
|
|
String& domainDnsName)
|
|
{
|
|
LOG_FUNCTION(ValidateDomainExists);
|
|
ASSERT(Win::IsWindow(dialog));
|
|
ASSERT(editResId > 0);
|
|
|
|
String name =
|
|
domainName.empty()
|
|
? Win::GetTrimmedDlgItemText(dialog, editResId)
|
|
: domainName;
|
|
|
|
// The invoking code should verify this condition, but we will handle
|
|
// it just in case.
|
|
|
|
ASSERT(!name.empty());
|
|
|
|
domainDnsName.erase();
|
|
|
|
Win::WaitCursor cursor;
|
|
|
|
bool valid = false;
|
|
DOMAIN_CONTROLLER_INFO* info = 0;
|
|
|
|
do
|
|
{
|
|
if (name.empty())
|
|
{
|
|
popup.Gripe(
|
|
dialog,
|
|
editResId,
|
|
String::load(IDS_MUST_ENTER_DOMAIN));
|
|
break;
|
|
}
|
|
|
|
// determine whether we can reach a DC for the domain, and whether it is
|
|
// a DS dc, and whether the name we're validating is truly the DNS name
|
|
// of the domain.
|
|
|
|
LOG(L"Validating " + name);
|
|
HRESULT hr =
|
|
MyDsGetDcName(
|
|
0,
|
|
name,
|
|
|
|
// force discovery to ensure that we don't pick up a cached
|
|
// entry for a domain that may no longer exist
|
|
|
|
DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
|
|
info);
|
|
if (FAILED(hr) || !info)
|
|
{
|
|
ShowDcNotFoundErrorDialog(
|
|
dialog,
|
|
editResId,
|
|
name,
|
|
String::load(IDS_WIZARD_TITLE),
|
|
String::format(IDS_DC_NOT_FOUND, name.c_str()),
|
|
|
|
// name might be netbios
|
|
|
|
false);
|
|
|
|
break;
|
|
}
|
|
|
|
if (!(info->Flags & DS_DS_FLAG))
|
|
{
|
|
// domain is not a DS domain, or the locator could not find a DS DC
|
|
// for that domain, so the candidate name is bad
|
|
|
|
ShowDcNotFoundErrorDialog(
|
|
dialog,
|
|
editResId,
|
|
name,
|
|
String::load(IDS_WIZARD_TITLE),
|
|
String::format(IDS_DC_NOT_FOUND, name.c_str()),
|
|
|
|
// name might be netbios
|
|
|
|
false);
|
|
|
|
break;
|
|
}
|
|
|
|
LOG(name + L" refers to DS domain");
|
|
|
|
// here we rely on the fact that if DsGetDcName is provided a flat
|
|
// domain name, then info->DomainName will also be the (same,
|
|
// normalized) flat name. Likewise, if provided a DNS domain name,
|
|
// info->DomainName will be the (same, normalized) DNS domain name.
|
|
|
|
if (info->Flags & DS_DNS_DOMAIN_FLAG)
|
|
{
|
|
// we can infer that name is a DNS domain name, since
|
|
// info->DomainName is a DNS domain name.
|
|
|
|
LOG(L"name is the DNS name");
|
|
ASSERT(
|
|
Dns::CompareNames(name, info->DomainName)
|
|
== DnsNameCompareEqual);
|
|
|
|
valid = true;
|
|
break;
|
|
}
|
|
|
|
LOG(name + L" is not the DNS domain name");
|
|
|
|
// the candidate name is not the DNS name of the domain. Make another
|
|
// call to DsGetDcName to determine the DNS domain name so we can get
|
|
// the user to confirm.
|
|
|
|
DOMAIN_CONTROLLER_INFO* info2 = 0;
|
|
hr = MyDsGetDcName(0, name, DS_RETURN_DNS_NAME, info2);
|
|
if (FAILED(hr) || !info2)
|
|
{
|
|
ShowDcNotFoundErrorDialog(
|
|
dialog,
|
|
editResId,
|
|
name,
|
|
String::load(IDS_WIZARD_TITLE),
|
|
String::format(IDS_DC_NOT_FOUND, name.c_str()),
|
|
|
|
// name is probably netbios
|
|
false);
|
|
|
|
break;
|
|
}
|
|
|
|
String message =
|
|
String::format(
|
|
IDS_CONFIRM_DNS_NAME,
|
|
name.c_str(),
|
|
info2->DomainName);
|
|
|
|
if (
|
|
popup.MessageBox(
|
|
dialog,
|
|
message,
|
|
MB_YESNO) == IDYES)
|
|
{
|
|
domainDnsName = info2->DomainName;
|
|
|
|
// The user accept the dns name as the name he meant to enter. As one
|
|
// last step, we call DsGetDcName with the dns domain name. If this
|
|
// fails, then we are in the situation where a DC can be found with
|
|
// netbios but not dns. So the user has a dns configuration problem.
|
|
// 28298
|
|
|
|
DOMAIN_CONTROLLER_INFO* info3 = 0;
|
|
hr =
|
|
MyDsGetDcName(
|
|
0,
|
|
domainDnsName,
|
|
|
|
// force discovery to ensure that we don't pick up a cached
|
|
// entry for a domain that may no longer exist
|
|
|
|
DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_PREFERRED,
|
|
info3);
|
|
if (FAILED(hr) || !info3)
|
|
{
|
|
ShowDcNotFoundErrorDialog(
|
|
dialog,
|
|
editResId,
|
|
domainDnsName,
|
|
String::load(IDS_WIZARD_TITLE),
|
|
String::format(IDS_DC_NOT_FOUND, domainDnsName.c_str()),
|
|
|
|
// we know the name is not netbios
|
|
|
|
true);
|
|
|
|
domainDnsName.erase();
|
|
break;
|
|
}
|
|
|
|
::NetApiBufferFree(info3);
|
|
valid = true;
|
|
}
|
|
|
|
// the user rejected the dns name, so they are admitting that what
|
|
// they entered was bogus. Don't pop up an error box in this case,
|
|
// as we have pestered the user enough.
|
|
|
|
::NetApiBufferFree(info2);
|
|
}
|
|
while (0);
|
|
|
|
if (info)
|
|
{
|
|
::NetApiBufferFree(info);
|
|
}
|
|
|
|
#ifdef DBG
|
|
if (!valid)
|
|
{
|
|
ASSERT(domainDnsName.empty());
|
|
}
|
|
#endif
|
|
|
|
return valid;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateDomainDoesNotExist(
|
|
HWND dialog,
|
|
int editResID)
|
|
{
|
|
return ValidateDomainDoesNotExist(dialog, String(), editResID);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ValidateDomainDoesNotExist(
|
|
HWND dialog,
|
|
const String& domainName,
|
|
int editResID)
|
|
{
|
|
LOG_FUNCTION(ValidateDomainDoesNotExist);
|
|
ASSERT(Win::IsWindow(dialog));
|
|
ASSERT(editResID > 0);
|
|
|
|
// this can take awhile.
|
|
|
|
Win::WaitCursor cursor;
|
|
|
|
String name =
|
|
domainName.empty()
|
|
? Win::GetTrimmedDlgItemText(dialog, editResID)
|
|
: domainName;
|
|
|
|
// The invoking code should verify this condition, but we will handle
|
|
// it just in case.
|
|
|
|
ASSERT(!name.empty());
|
|
|
|
bool valid = true;
|
|
String message;
|
|
do
|
|
{
|
|
if (name.empty())
|
|
{
|
|
message = String::load(IDS_MUST_ENTER_DOMAIN);
|
|
valid = false;
|
|
break;
|
|
}
|
|
if (IsDomainReachable(name) || DS::IsDomainNameInUse(name))
|
|
{
|
|
message = String::format(IDS_DOMAIN_NAME_IN_USE, name.c_str());
|
|
valid = false;
|
|
break;
|
|
}
|
|
|
|
// otherwise the domain does not exist
|
|
}
|
|
while (0);
|
|
|
|
if (!valid)
|
|
{
|
|
popup.Gripe(dialog, editResID, message);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DisableConsoleLocking()
|
|
{
|
|
LOG_FUNCTION(disableConsoleLocking);
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
BOOL screenSaverEnabled = FALSE;
|
|
|
|
hr =
|
|
|
|
// REVIEWED-2002/02/27-sburns the docs are vague, but we know that
|
|
// screenSaverEnabled as a BOOL works.
|
|
|
|
Win::SystemParametersInfo(
|
|
SPI_GETSCREENSAVEACTIVE,
|
|
0,
|
|
&screenSaverEnabled,
|
|
0);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
if (screenSaverEnabled)
|
|
{
|
|
// disable it.
|
|
|
|
screenSaverEnabled = FALSE;
|
|
hr =
|
|
|
|
// ISSUE-2002/02/27-sburns it looks to me from the docs that the
|
|
// second param should be used, and not the third
|
|
|
|
Win::SystemParametersInfo(
|
|
SPI_SETSCREENSAVEACTIVE,
|
|
0,
|
|
&screenSaverEnabled,
|
|
SPIF_SENDCHANGE);
|
|
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
|
|
// turn off lock computer option in winlogon
|
|
|
|
do
|
|
{
|
|
RegistryKey key;
|
|
|
|
hr =
|
|
key.Create(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// '2' means "disable for this session, reset to 0 on reboot."
|
|
|
|
hr = key.SetValue(L"DisableLockWorkstation", 2);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
EnableConsoleLocking()
|
|
{
|
|
LOG_FUNCTION(EnableConsoleLocking);
|
|
|
|
#ifdef DBG
|
|
State& state = State::GetInstance();
|
|
ASSERT(
|
|
state.GetRunContext() != State::PDC_UPGRADE &&
|
|
state.GetRunContext() != State::BDC_UPGRADE);
|
|
#endif
|
|
|
|
// CODEWORK: we don't re-enable the screensaver (we need to remember
|
|
// if it was enabled when we called DisableConsoleLocking)
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
RegistryKey key;
|
|
|
|
hr =
|
|
key.Create(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon");
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// 0 means "enable"
|
|
|
|
hr = key.SetValue(L"DisableLockWorkstation", 0);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
CheckDiskSpace(const String& path, unsigned minSpaceMB)
|
|
{
|
|
LOG_FUNCTION(checkDiskSpace);
|
|
ASSERT(FS::IsValidPath(path));
|
|
|
|
String vol = FS::GetRootFolder(path);
|
|
|
|
ULONGLONG spaceInBytes;
|
|
|
|
// REVIEWED-2002/02/25-sburns byte count is correctly passed.
|
|
|
|
::ZeroMemory(&spaceInBytes, sizeof ULONGLONG);
|
|
|
|
HRESULT hr = FS::GetAvailableSpace(vol, spaceInBytes);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// prefast is happiest if we start out with the same-width integers
|
|
// before we shift one, hence the cast.
|
|
// NTRAID#NTBUG9-540413-2002/03/12-sburns
|
|
|
|
ULONGLONG spaceInMb = spaceInBytes / ( (ULONGLONG) 1 << 20);
|
|
|
|
if (spaceInMb >= minSpaceMB)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
String
|
|
GetFirstNtfs5HardDrive()
|
|
{
|
|
LOG_FUNCTION(GetFirstNtfs5HardDrive);
|
|
|
|
String result;
|
|
do
|
|
{
|
|
StringVector dl;
|
|
HRESULT hr = FS::GetValidDrives(std::back_inserter(dl));
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
ASSERT(dl.size());
|
|
for (
|
|
StringVector::iterator i = dl.begin();
|
|
i != dl.end();
|
|
++i)
|
|
{
|
|
LOG(*i);
|
|
|
|
if (
|
|
FS::GetFileSystemType(*i) == FS::NTFS5
|
|
&& Win::GetDriveType(*i) == DRIVE_FIXED )
|
|
{
|
|
// found one. good to go
|
|
|
|
LOG(String::format(L"%1 is NTFS5", i->c_str()));
|
|
|
|
result = *i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
LOG(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
ComputerWasRenamedAndNeedsReboot()
|
|
{
|
|
LOG_FUNCTION(ComputerWasRenamedAndNeedsReboot);
|
|
|
|
bool result = false;
|
|
|
|
do
|
|
{
|
|
String active = Computer::GetActivePhysicalNetbiosName();
|
|
String future = Computer::GetFuturePhysicalNetbiosName();
|
|
|
|
if (active.icompare(future) != 0)
|
|
{
|
|
// a name change is pending reboot.
|
|
|
|
LOG(L"netbios name was changed");
|
|
LOG(active);
|
|
LOG(future);
|
|
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
// At this point, the netbios names are the same, or there is no future
|
|
// netbios name. So check the DNS names.
|
|
|
|
if (IsTcpIpInstalled())
|
|
{
|
|
// DNS names only exist if tcp/ip is installed.
|
|
|
|
active = Computer::GetActivePhysicalFullDnsName();
|
|
future = Computer::GetFuturePhysicalFullDnsName();
|
|
|
|
if (Dns::CompareNames(active, future) == DnsNameCompareNotEqual)
|
|
{
|
|
LOG(L"dns name was changed");
|
|
LOG(active);
|
|
LOG(future);
|
|
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// At this point, we have confirmed that there is no pending name
|
|
// change.
|
|
|
|
LOG(L"No pending computer name change");
|
|
}
|
|
while (0);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
String
|
|
GetForestName(const String& domain, HRESULT* hrOut)
|
|
{
|
|
LOG_FUNCTION2(GetForestName, domain);
|
|
ASSERT(!domain.empty());
|
|
|
|
String dnsForestName;
|
|
|
|
DOMAIN_CONTROLLER_INFO* info = 0;
|
|
HRESULT hr =
|
|
MyDsGetDcName(
|
|
0,
|
|
domain,
|
|
DS_RETURN_DNS_NAME,
|
|
info);
|
|
if (SUCCEEDED(hr) && info)
|
|
{
|
|
ASSERT(info->DnsForestName);
|
|
|
|
if (info->DnsForestName)
|
|
{
|
|
dnsForestName = info->DnsForestName;
|
|
}
|
|
::NetApiBufferFree(info);
|
|
}
|
|
|
|
if (hrOut)
|
|
{
|
|
*hrOut = hr;
|
|
}
|
|
|
|
return dnsForestName;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
ValidatePasswordAgainstPolicy(
|
|
const EncryptedString& password,
|
|
const String& userName)
|
|
{
|
|
LOG_FUNCTION(ValidatePasswordAgainstPolicy);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
PWSTR cleartext = password.GetClearTextCopy();
|
|
|
|
NET_VALIDATE_PASSWORD_RESET_INPUT_ARG inArg;
|
|
::ZeroMemory(&inArg, sizeof inArg);
|
|
|
|
inArg.ClearPassword = cleartext;
|
|
|
|
// UserAccountName is used by one of the complexity checking tests to
|
|
// ensure that the candidate password does not contain that string
|
|
|
|
inArg.UserAccountName =
|
|
userName.empty()
|
|
|
|
// ISSUE-2002/04/19-sburns This should be null since UmitA has
|
|
// fixed his bug to allow this parameter to be null
|
|
|
|
? L"aklsdjiwuerowierlkmclknlaksjdqweiquroijlkasjlkq" // 0
|
|
: (PWSTR) userName.c_str();
|
|
|
|
// HashedPassword is only used when checking password histories.
|
|
|
|
NET_VALIDATE_OUTPUT_ARG* outArg = 0;
|
|
|
|
hr =
|
|
Win32ToHresult(
|
|
::NetValidatePasswordPolicy(
|
|
0,
|
|
0,
|
|
NetValidatePasswordReset,
|
|
&inArg,
|
|
(void**) &outArg));
|
|
|
|
password.DestroyClearTextCopy(cleartext);
|
|
|
|
if (SUCCEEDED(hr) && outArg)
|
|
{
|
|
hr = Win32ToHresult(outArg->ValidationStatus);
|
|
NetApiBufferFree(outArg);
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
IsValidPassword(
|
|
HWND dialog,
|
|
int passwordResID,
|
|
int confirmResID,
|
|
bool isForSafeMode,
|
|
EncryptedString& validatedPassword)
|
|
{
|
|
LOG_FUNCTION(IsValidPassword);
|
|
ASSERT(Win::IsWindow(dialog));
|
|
ASSERT(passwordResID);
|
|
ASSERT(confirmResID);
|
|
|
|
validatedPassword.Clear();
|
|
bool result = false;
|
|
int message = 0;
|
|
|
|
do
|
|
{
|
|
EncryptedString password =
|
|
Win::GetEncryptedDlgItemText(dialog, passwordResID);
|
|
EncryptedString confirm =
|
|
Win::GetEncryptedDlgItemText(dialog, confirmResID);
|
|
|
|
if (password != confirm)
|
|
{
|
|
message = IDS_PASSWORD_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
State& state = State::GetInstance();
|
|
|
|
if (
|
|
isForSafeMode
|
|
&& password.IsEmpty()
|
|
&& state.RunHiddenUnattended()
|
|
&& !state.IsSafeModeAdminPwdOptionPresent() )
|
|
{
|
|
// password is blank, we're running with an answerfile, and the
|
|
// user did not specify a password in the answerfile. In this
|
|
// case, we will use the local admin password as the domain admin
|
|
// password. So we should skip the policy check.
|
|
// NTRAID#NTBUG9-619502-2002/05/09-sburns
|
|
|
|
// this branch empty on purpose.
|
|
}
|
|
else
|
|
{
|
|
// CODEWORK: in v2 of this code, extract the name of the local admin
|
|
// sid
|
|
|
|
HRESULT hr = ValidatePasswordAgainstPolicy(password, String());
|
|
if (FAILED(hr))
|
|
{
|
|
// we translate the error codes into our own message IDs because
|
|
// the system error messages are too horrible to bear, and those
|
|
// that own them aren't inclined to change them.
|
|
|
|
switch (HRESULT_CODE(hr))
|
|
{
|
|
case NERR_PasswordTooShort:
|
|
{
|
|
message = IDS_PASSWORD_TOO_SHORT;
|
|
break;
|
|
}
|
|
case NERR_PasswordTooLong:
|
|
{
|
|
message = IDS_PASSWORD_TOO_LONG;
|
|
break;
|
|
}
|
|
case NERR_PasswordNotComplexEnough:
|
|
{
|
|
message = IDS_PASSWORD_TOO_SIMPLE;
|
|
break;
|
|
}
|
|
case NERR_PasswordFilterError:
|
|
{
|
|
message = IDS_PASSWORD_TOO_COARSE;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// in this case, the validation itself failed, so we accept
|
|
// the candidate password. Note that this is no worse than
|
|
// not checking the policy in the first place, and the
|
|
// ramifications are that the account will get a possibly
|
|
// weaker password than policy would like.
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (message)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
result = true;
|
|
|
|
// at this point, the password has successfully run the gauntlet.
|
|
|
|
validatedPassword = password;
|
|
}
|
|
while (0);
|
|
|
|
if (!result)
|
|
{
|
|
String blank;
|
|
Win::SetDlgItemText(dialog, passwordResID, blank);
|
|
Win::SetDlgItemText(dialog, confirmResID, blank);
|
|
popup.Gripe(dialog, passwordResID, message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|