windows-nt/Source/XPSP1/NT/net/config/netcfg/nwclicfg/nwcliobj.cpp
2020-09-26 16:20:57 +08:00

859 lines
24 KiB
C++

//
// N W C L I O B J . C P P
//
// Implementation of the CNWClient notify object model
//
#include "pch.h"
#pragma hdrstop
#include "ncerror.h"
#include "ncperms.h"
#include "ncreg.h"
#include "ncsetup.h"
#include "ncsvc.h"
#include "nwcliobj.h"
#include <ncshell.h>
extern const WCHAR c_szAfNWCWorkstationParameters[];
extern const WCHAR c_szAfNWCWorkstationShares[];
extern const WCHAR c_szAfNWCWorkstationDrives[];
extern const WCHAR c_szInfId_MS_NWIPX[];
extern const WCHAR c_szInfId_MS_Server[];
//---[ Constants ]-------------------------------------------------------------
static const WCHAR c_szNWClientParamPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters";
static const WCHAR c_szNWClientSharesPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Shares";
static const WCHAR c_szNWClientDrivesPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Drives";
static const WCHAR c_szLMServerParamPath[] = L"System\\CurrentControlSet\\Services\\LanmanServer\\Parameters";
static const WCHAR c_szLMServerLinkagePath[] = L"System\\CurrentControlSet\\Services\\LanmanServer\\Linkage";
static const WCHAR c_szEnableSharedNetDrives[] = L"EnableSharedNetDrives";
static const WCHAR c_szOtherDependencies[] = L"OtherDependencies";
static const WCHAR c_szGWEnabledValue[] = L"GatewayEnabled";
extern const WCHAR c_szSvcLmServer[]; // L"LanmanServer";
extern const WCHAR c_szSvcNWCWorkstation[]; // L"NWCWorkstation";
HRESULT HrRefreshEntireNetwork();
HRESULT HrGetEntireNetworkPidl(LPITEMIDLIST *ppidlFolder);
//
// Constructor
//
CNWClient::CNWClient()
{
// Initialize member variables.
m_pnc = NULL;
m_pncc = NULL;
m_eInstallAction = eActUnknown;
m_hlibConfig = NULL;
m_fUpgrade = FALSE;
// Get the product flavor (PF_WORKSTATION or PF_SERVER). Use this
// to decide whether or not we need to install the "server" component.
//
GetProductFlavor(NULL, &m_pf);
}
CNWClient::~CNWClient()
{
ReleaseObj(m_pncc);
ReleaseObj(m_pnc);
// Release KEY handles here.
}
//
// INetCfgNotify
//
STDMETHODIMP CNWClient::Initialize( INetCfgComponent * pnccItem,
INetCfg* pnc,
BOOL fInstalling)
{
Validate_INetCfgNotify_Initialize(pnccItem, pnc, fInstalling);
TraceTag(ttidNWClientCfg, "CNWClient::Initialize");
m_pncc = pnccItem;
m_pnc = pnc;
AssertSz(m_pncc, "m_pncc NULL in CNWClient::Initialize");
AssertSz(m_pnc, "m_pnc NULL in CNWClient::Initialize");
// Addref the config objects
//
AddRefObj(m_pncc);
AddRefObj(m_pnc);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// Member: CNWClient::HrRestoreRegistry
//
// Purpose: Restores the contents of the registry for this component
//
// Arguments:
// (none)
//
// Returns: Win32 error if failed, otherwise S_OK
//
// Author: jeffspr 13 Aug 1997
//
// Notes:
//
HRESULT CNWClient::HrRestoreRegistry()
{
HRESULT hr = S_OK;
HKEY hkey = NULL;
TOKEN_PRIVILEGES * ptpRestore = NULL;
DWORD dwDisp = 0;
static const WCHAR c_szSvcDLLName[] = L"%SystemRoot%\\System32\\nwwks.dll";
static const WCHAR c_szServiceDll[] = L"ServiceDll";
TraceTag(ttidNWClientCfg, "CNWClient::HrRestoreRegistry");
if (!m_strParamsRestoreFile.empty() ||
!m_strDrivesRestoreFile.empty() ||
!m_strSharesRestoreFile.empty())
{
hr = HrEnableAllPrivileges(&ptpRestore);
}
if (SUCCEEDED(hr) && !m_strParamsRestoreFile.empty())
{
// Ensure key is there by creating it
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientParamPath, 0,
KEY_ALL_ACCESS, NULL, &hkey, &dwDisp);
if (SUCCEEDED(hr))
{
hr = HrRegRestoreKey(hkey, m_strParamsRestoreFile.c_str(), 0);
if (FAILED(hr))
{
TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for "
"Parameters", hr);
hr = S_OK;
}
//
// Bug 182442. HrRegRestoreKey above overwrites the ServiceDll value added
// from the inf file. So, we manually save it.
//
hr = HrRegSetValueEx(hkey, c_szServiceDll, REG_EXPAND_SZ,
(const BYTE *)c_szSvcDLLName,
(wcslen(c_szSvcDLLName) + 1) * sizeof(WCHAR));
if (FAILED(hr))
{
TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for "
"ServiceDll", hr);
hr = S_OK;
}
RegCloseKey(hkey);
hkey = NULL;
}
}
if (!m_strSharesRestoreFile.empty())
{
// Ensure key is there by creating it
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientSharesPath, 0,
KEY_ALL_ACCESS, NULL, &hkey, &dwDisp);
if (SUCCEEDED(hr))
{
hr = HrRegRestoreKey(hkey, m_strSharesRestoreFile.c_str(), 0);
if (FAILED(hr))
{
TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for "
"Shares", hr);
hr = S_OK;
}
RegCloseKey(hkey);
hkey = NULL;
}
}
if (!m_strDrivesRestoreFile.empty())
{
// Ensure key is there by creating it
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientDrivesPath, 0,
KEY_ALL_ACCESS, NULL, &hkey, &dwDisp);
if (SUCCEEDED(hr))
{
hr = HrRegRestoreKey(hkey, m_strDrivesRestoreFile.c_str(), 0);
if (FAILED(hr))
{
TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for "
"Drives", hr);
hr = S_OK;
}
RegCloseKey(hkey);
hkey = NULL;
}
}
if (ptpRestore)
{
hr = HrRestorePrivileges(ptpRestore);
delete [] reinterpret_cast<BYTE *>(ptpRestore);
}
TraceError("CNWClient::HrRestoreRegistry", hr);
return hr;
}
static const WCHAR c_szDefaultLocation[] = L"DefaultLocation";
static const WCHAR c_szDefaultScriptOptions[] = L"DefaultScriptOptions";
HRESULT CNWClient::HrWriteAnswerFileParams()
{
HRESULT hr = S_OK;
TraceTag(ttidNWClientCfg, "CNWClient::HrWriteAnswerFileParams");
// Don't do anything if we don't have anything to write to the
// registry
if (!m_strDefaultLocation.empty() || (m_dwLogonScript != 0xFFFFFFFF))
{
HKEY hkey;
DWORD dwDisp;
// Ensure key is there by creating it
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientParamPath, 0,
KEY_ALL_ACCESS, NULL, &hkey, &dwDisp);
if (SUCCEEDED(hr))
{
if (!m_strDefaultLocation.empty())
{
hr = HrRegSetString(hkey, c_szDefaultLocation,
m_strDefaultLocation);
if (FAILED(hr))
{
TraceError("CNWClient::HrWriteAnswerFileParams - Couldn't"
" set DefaultLocation", hr);
hr = S_OK;
}
}
if (m_dwLogonScript != 0xFFFFFFFF)
{
// 0x3 is combination of the following:
//
// #define NW_LOGONSCRIPT_DISABLED 0x00000000
// #define NW_LOGONSCRIPT_ENABLED 0x00000001
// #define NW_LOGONSCRIPT_4X_ENABLED 0x00000002
//
hr = HrRegSetDword(hkey, c_szDefaultScriptOptions,
m_dwLogonScript ? 0x3 : 0x0);
if (FAILED(hr))
{
TraceError("CNWClient::HrWriteAnswerFileParams - Couldn't"
" set DefaultLocation", hr);
hr = S_OK;
}
}
RegCloseKey(hkey);
}
}
TraceError("CNWClient::HrWriteAnswerFileParams", hr);
return hr;
}
static const WCHAR c_szPreferredServer[] = L"PreferredServer";
static const WCHAR c_szDefaultTree[] = L"DefaultTree";
static const WCHAR c_szDefaultContext[] = L"DefaultContext";
static const WCHAR c_szLogonScript[] = L"LogonScript";
//+---------------------------------------------------------------------------
//
// Member: CNWClient::HrProcessAnswerFile
//
// Purpose: Handles necessary processing of contents of the answer file.
//
// Arguments:
// pszAnswerFile [in] Filename of answer file for upgrade.
// pszAnswerSection [in] Comma-separated list of sections in the
// file appropriate to this component.
//
// Returns: S_OK if successful, setup API error otherwise.
//
// Author: jeffspr 8 May 1997
//
// Notes:
//
HRESULT CNWClient::HrProcessAnswerFile( PCWSTR pszAnswerFile,
PCWSTR pszAnswerSection)
{
HRESULT hr;
CSetupInfFile csif;
TraceTag(ttidNWClientCfg, "CNWClient::HrProcessAnswerFile");
// Open the answer file.
hr = csif.HrOpen(pszAnswerFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
if (FAILED(hr))
{
hr = S_OK;
goto Exit;
}
// Restore portions of the registry based on file names from the answer
// file
// Get restore file for "Parameters" key
hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationParameters,
&m_strParamsRestoreFile);
if (FAILED(hr))
{
TraceError("CNWClient::HrProcessAnswerFile - Error restoring "
"Parameters key", hr);
// oh well, just continue
hr = S_OK;
}
// Get restore file for "Shares" key
hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationShares,
&m_strSharesRestoreFile);
if (FAILED(hr))
{
TraceError("CNWClient::HrProcessAnswerFile - Error restoring "
"Shares key", hr);
// oh well, just continue
hr = S_OK;
}
// Get restore file for "Drives" key
hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationDrives,
&m_strDrivesRestoreFile);
if (FAILED(hr))
{
TraceError("CNWClient::HrProcessAnswerFile - Error restoring "
"Drives key", hr);
// oh well, just continue
hr = S_OK;
}
//
// Read answer file parameters (these are all optional so no errors are
// saved)
//
TraceTag(ttidNWClientCfg, "Reading PreferredServer from answer file");
// Read contents of PreferredServer key.
if (FAILED(csif.HrGetString(pszAnswerSection, c_szPreferredServer,
&m_strDefaultLocation)))
{
// Couldn't read PreferredServer key, so we must assume that the other
// two values are present
tstring strDefaultTree;
tstring strDefaultContext;
TraceTag(ttidNWClientCfg, "PreferredServer not found so trying "
"DefaultTree and DefaultContext instead");
// Read contents of DefaultTree key.
if (SUCCEEDED(csif.HrGetString(pszAnswerSection, c_szDefaultTree,
&strDefaultTree)))
{
TraceTag(ttidNWClientCfg, "Got DefaultTree ok: %S",
strDefaultTree.c_str());
// Read contents of DefaultContext key.
hr = csif.HrGetString(pszAnswerSection, c_szDefaultContext,
&strDefaultContext);
if (SUCCEEDED(hr))
{
TraceTag(ttidNWClientCfg, "Got DefaultContext ok: %S",
strDefaultContext.c_str());
// Munge the DefaultLocation value with the DefaultTree and
// DefaultContext values read from the answer file
m_strDefaultLocation = L"*";
m_strDefaultLocation += strDefaultTree;
m_strDefaultLocation += L"\\";
m_strDefaultLocation += strDefaultContext;
TraceTag(ttidNWClientCfg, "DefaultLocation is: %S",
m_strDefaultLocation.c_str());
}
else
{
TraceError("CNWClient::HrProcessAnswerFile - error reading "
"DefaultContext", hr);
hr = S_OK;
}
}
}
else
{
TraceTag(ttidNWClientCfg, "DefaultLocation is: %S",
m_strDefaultLocation.c_str());
}
// Init to impossible value so we know whether we read it or not
m_dwLogonScript = 0xFFFFFFFF;
// Read contents of LogonScript key.
(VOID) csif.HrGetStringAsBool(pszAnswerSection, c_szLogonScript,
reinterpret_cast<BOOL *>(&m_dwLogonScript));
TraceTag(ttidNWClientCfg, "LogonScript is: %ld", m_dwLogonScript);
Exit:
TraceError("CNWClient::HrProcessAnswerFile", hr);
return hr;
}
STDMETHODIMP CNWClient::Upgrade(DWORD dwSetupFlags, DWORD dwUpgradeFromBuildNo)
{
return S_FALSE;
}
STDMETHODIMP CNWClient::ReadAnswerFile(PCWSTR pszAnswerFile,
PCWSTR pszAnswerSection)
{
Validate_INetCfgNotify_ReadAnswerFile(pszAnswerFile,
pszAnswerSection);
TraceTag(ttidNWClientCfg, "CNWClient::ReadAnswerFile");
m_eInstallAction = eActInstall;
// If we're not already installed, do the work.
//
if (pszAnswerFile && pszAnswerSection)
{
HRESULT hr = HrProcessAnswerFile(pszAnswerFile, pszAnswerSection);
if (FAILED(hr))
{
TraceError("CNWClient::NetworkInstall - Answer file has errors. Defaulting "
"all information as if answer file did not exist.",
hr);
}
}
return S_OK;
}
STDMETHODIMP CNWClient::Install(DWORD dw)
{
Validate_INetCfgNotify_Install(dw);
TraceTag(ttidNWClientCfg, "CNWClient::Install");
m_eInstallAction = eActInstall;
// Install the NWLink sub-component
HRESULT hr = HrInstallComponentOboComponent(m_pnc, NULL,
GUID_DEVCLASS_NETTRANS,
c_szInfId_MS_NWIPX,
m_pncc,
NULL);
if (SUCCEEDED(hr))
{
// If we're NT Server, we DO need to install it, as what we're
// installing is GSNW, not CSNW (and therefore, since we're sharing
// resources, we need to use the server service)
//
if (PF_SERVER == m_pf)
{
NETWORK_INSTALL_PARAMS nip;
nip.dwSetupFlags = dw;
nip.dwUpgradeFromBuildNo = -1;
nip.pszAnswerFile = NULL;
nip.pszAnswerSection = NULL;
// Install Server
hr = HrInstallComponentOboComponent(m_pnc, &nip,
GUID_DEVCLASS_NETSERVICE,
c_szInfId_MS_Server,
m_pncc,
NULL);
}
}
TraceError("CNWClient::Install", hr);
return hr;
}
STDMETHODIMP CNWClient::Removing()
{
TraceTag(ttidNWClientCfg, "CNWClient::Removing");
m_eInstallAction = eActRemove;
// Remove the NWLink service
//
HRESULT hr = HrRemoveComponentOboComponent(m_pnc,
GUID_DEVCLASS_NETTRANS,
c_szInfId_MS_NWIPX,
m_pncc);
if (SUCCEEDED(hr))
{
if (PF_SERVER == m_pf)
{
// Remove our reference of the Server service
//
hr = HrRemoveComponentOboComponent(m_pnc,
GUID_DEVCLASS_NETSERVICE,
c_szInfId_MS_Server,
m_pncc);
}
}
if (hr == NETCFG_S_STILL_REFERENCED)
{
// If services are still in use, that's OK, I just needed to make
// sure that I released my reference.
//
hr = S_OK;
}
Validate_INetCfgNotify_Removing_Return(hr);
TraceError("CNWClient::Removing()", hr);
return hr;
}
STDMETHODIMP CNWClient::Validate()
{
return S_OK;
}
STDMETHODIMP CNWClient::CancelChanges()
{
return S_OK;
}
STDMETHODIMP CNWClient::ApplyRegistryChanges()
{
HRESULT hr = S_OK;
TraceTag(ttidNWClientCfg, "CNWClient::ApplyRegistryChanges");
if (m_eInstallAction == eActRemove)
{
hr = HrRemoveCodeFromOldINF();
}
else if (m_eInstallAction == eActInstall)
{
hr = HrRestoreRegistry();
if (FAILED(hr))
{
TraceError("CNWClient::ApplyRegistryChanges - HrRestoreRegistry non-fatal error",
hr);
hr = S_OK;
}
hr = HrWriteAnswerFileParams();
if (FAILED(hr))
{
TraceError("CNWClient::ApplyRegistryChanges - HrWriteAnswerFileParams "
"non-fatal error", hr);
hr = S_OK;
}
// If gateway is enabled, modify lanmanserver appropriately
// Ignore the return code other than to trace it.
//
hr = HrEnableGatewayIfNeeded();
if (FAILED(hr))
{
TraceError("CNWClient::ApplyRegistryChanges - HrEnableGatewayIfNeeded non-fatal error", hr);
}
hr = HrInstallCodeFromOldINF();
}
Validate_INetCfgNotify_Apply_Return(hr);
TraceError("CNWClient::ApplyRegistryChanges",
(hr == S_FALSE) ? S_OK : hr);
return hr;
}
STDMETHODIMP CNWClient::ApplyPnpChanges (
INetCfgPnpReconfigCallback* pICallback)
{
HRESULT hr;
hr = HrRefreshEntireNetwork();
if (FAILED(hr))
{
TraceError("CNWClient::ApplyPnpChanges - HrRefreshEntireNetwork"
"non-fatal error", hr);
hr = S_OK;
}
// GlennC can't do the work to make NW Client PnP so we're forced to
// prompt for a reboot for any change.
//
return NETCFG_S_REBOOT;
}
// Note -- Don't convert this to a constant. We need copies of it within the
// functions because ParseDisplayName actually mangles the string.
//
#define ENTIRE_NETWORK_PATH L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork"
//+---------------------------------------------------------------------------
//
// Function: HrGetEntireNetworkPidl
//
// Purpose: Get the pidl for "Entire Network". Used in places where we're
// not folder specific, but we still need to update folder
// entries.
//
// Arguments:
// ppidlFolder [out] Return parameter for the folder pidl
//
// Returns:
//
// Author: anbrad 08 Jun 1999
// jeffspr 13 Jun 1998
//
// Notes:
//
HRESULT HrGetEntireNetworkPidl(LPITEMIDLIST *ppidlFolder)
{
HRESULT hr = S_OK;
LPSHELLFOLDER pshf = NULL;
LPITEMIDLIST pidlFolder = NULL;
Assert(ppidlFolder);
WCHAR szEntireNetworkPath[] = ENTIRE_NETWORK_PATH;
// Get the desktop folder, so we can parse the display name and get
// the UI object of the connections folder
//
hr = SHGetDesktopFolder(&pshf);
if (SUCCEEDED(hr))
{
ULONG chEaten;
hr = pshf->ParseDisplayName(NULL, 0, (WCHAR *) szEntireNetworkPath,
&chEaten, &pidlFolder, NULL);
ReleaseObj(pshf);
}
// If succeeded, fill in the return param.
//
if (SUCCEEDED(hr))
{
*ppidlFolder = pidlFolder;
}
else
{
// If we failed, then delete the pidl if we already got it.
//
if (pidlFolder)
SHFree(pidlFolder);
}
TraceHr(ttidNWClientCfg, FAL, hr, FALSE, "HrGetEntireNetworkPidl");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrRefreshEntireNetwork
//
// Purpose: Update the "Entire Network" portion of the shell due to
// the addition of a new networking client (NWClient)
//
// Arguments:
// (none)
//
// Returns:
//
// Author: anbrad 08 Jun 1999
//
// Notes:
//
HRESULT HrRefreshEntireNetwork()
{
HRESULT hr = S_OK;
HCURSOR hcWait = SetCursor(LoadCursor(NULL, IDC_WAIT));
LPITEMIDLIST pidlFolder = NULL;;
hr = HrGetEntireNetworkPidl(&pidlFolder);
// If we now have a pidl, send the GenerateEvent to update the item
//
if (SUCCEEDED(hr))
{
Assert(pidlFolder);
// SHCNE_UPDATEDIR?ITEM
GenerateEvent(SHCNE_UPDATEDIR, pidlFolder, NULL, NULL);
}
if (hcWait)
{
SetCursor(hcWait);
}
if (pidlFolder)
{
SHFree(pidlFolder);
}
TraceHr(ttidError, FAL, hr, FALSE, "HrRefreshEntireNetwork");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrEnableGatewayIfNeeded
//
// Purpose: Update the Lanman dependencies, if appropriate (meaning if
// gateway is enabled).
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 19 Aug 1999
//
// Notes:
//
HRESULT CNWClient::HrEnableGatewayIfNeeded()
{
HRESULT hr = S_OK;
HKEY hKey = NULL;
DWORD dwValue = 0;
CServiceManager sm;
CService svc;
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE,
c_szNWClientParamPath,
KEY_READ,
&hKey);
if (FAILED(hr))
{
TraceError("Couldn't open NWClient param key", hr);
goto Exit;
}
hr = HrRegQueryDword(hKey,
c_szGWEnabledValue,
&dwValue);
if (FAILED(hr))
{
if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
TraceError("Couldn't query the GWEnabled Value", hr);
goto Exit;
}
else
{
dwValue = 0;
}
}
else
{
// Normalize to bool
//
dwValue = !!dwValue;
}
RegSafeCloseKey(hKey);
hKey = NULL;
// If there are gateway services present, then add the dependencies
// to LanmanServer
//
if (dwValue > 0)
{
// Set the value in the registry for the server paramaters.
//
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE,
c_szLMServerParamPath,
KEY_WRITE,
&hKey);
if (SUCCEEDED(hr))
{
hr = HrRegSetDword(hKey,
c_szEnableSharedNetDrives,
dwValue);
RegSafeCloseKey(hKey);
hKey = NULL;
}
hr = sm.HrOpen();
if (SUCCEEDED(hr))
{
hr = sm.HrOpenService(&svc, c_szSvcLmServer, NO_LOCK);
if (SUCCEEDED(hr))
{
// Add dependency of NWC Workstation to Server
//
hr = sm.HrAddServiceDependency(c_szSvcLmServer,
c_szSvcNWCWorkstation);
if (SUCCEEDED(hr))
{
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE,
c_szLMServerLinkagePath,
KEY_READ | KEY_WRITE,
&hKey);
if (SUCCEEDED(hr))
{
// Add the "OtherDependencies" to LanmanServer for legacy reasons
//
hr = HrRegAddStringToMultiSz(c_szSvcNWCWorkstation,
hKey,
NULL,
c_szOtherDependencies,
STRING_FLAG_ENSURE_AT_END | STRING_FLAG_DONT_MODIFY_IF_PRESENT,
0);
RegSafeCloseKey(hKey);
hKey = NULL;
}
}
}
else
{
TraceError("Failed to open LanmanServer service for dependency mods", hr);
}
}
else
{
TraceError("Failed to open service control manager", hr);
}
}
Exit:
TraceHr(ttidNWClientCfg, FAL, hr, FALSE, "HrEnableGatewayIfNeeded");
return hr;
}