Windows-Server-2003/net/winnet/enum.cxx

2754 lines
79 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
enum.cxx
Abstract:
Contains the entry points for the WinNet Enum API supported by the
Multi-Provider Router. The following functions are in this file:
WNetOpenEnumW
WNetEnumResourceW
WNetCloseEnum
MprOpenEnumConnect
MprOpenEnumNetwork
MprEnumConnect
MprEnumNetwork
MprProviderEnum
MprCopyResource
MprCopyProviderEnum
MprProviderOpen
MprOpenRemember
MprEnumRemembered
MprMultiStrBuffSize
Author:
Dan Lafferty (danl) 14-Oct-1991
Environment:
User Mode -Win32
Notes:
Revision History:
14-Oct-1991 danl
created
21-Sep-1992 KeithMo
Handle odd-sized buffers.
02-Nov-1992 danl
Fail with NO_NETWORK if there are no providers.
02-Mar-1995 anirudhs
Add support for RESOURCE_CONTEXT.
17-Jul-1995 anirudhs
Add recognition (but not true support) of RESOURCE_RECENT.
Clean up code for detecting top-level enum.
03-Aug-1995 anirudhs
WNetEnumResourceW: Allow a *lpcCount of 0.
15-Sep-1995 anirudhs
MprEnumRemembered: Fail after all resources have been enumerated.
24-Sep-1995 anirudhs
Add support for customization of the RESOURCE_CONTEXT enumeration
based on policy settings.
11-Apr-1996 anirudhs
Use CRoutedOperation in one case of WNetOpenEnumW.
16-Mar-1999 jschwart
Add support for RESOURCE_SHAREABLE
05-May-1999 jschwart
Make provider addition/removal dynamic
--*/
//
// INCLUDES
//
#include "precomp.hxx"
#include <memory.h> // memcpy
#include <lmcons.h> // needed for netlib.h
#include <regstr.h> // Registry keys and value names
//
// EXTERNALS
//
extern DWORD GlobalNumActiveProviders;
extern HMODULE hDLL;
//
// DATA STRUCTURES
//
//
// "Manually" align headers and put pointers first in ENUM
// structures to avoid Win64 alignment faults. Keep Key as
// the first field in the header so MPR knows where to check
// to see what type of enum it is.
//
typedef struct _CONNECT_HEADER
{
DWORD Key;
DWORD ReturnRoot;
DWORD dwNumProviders;
DWORD dwNumActiveProviders;
}
CONNECT_HEADER, *LPCONNECT_HEADER;
typedef struct _CONNECT_ENUM
{
HANDLE ProviderEnumHandle;
HINSTANCE hProviderDll; // Refcount the provider DLL
PF_NPEnumResource pfEnumResource;
PF_NPCloseEnum pfCloseEnum;
DWORD State;
}
CONNECT_ENUM, *LPCONNECT_ENUM;
typedef struct _NETWORK_HEADER
{
DWORD Key;
DWORD dwNumProviders;
DWORD dwNumActiveProviders;
DWORD dwPad;
}
NETWORK_HEADER, *LPNETWORK_HEADER;
typedef struct _NETWORK_ENUM
{
HINSTANCE hProviderDll;
LPNETRESOURCE lpnr;
DWORD State;
}
NETWORK_ENUM, *LPNETWORK_ENUM;
typedef struct _ENUM_HANDLE
{
DWORD Key;
DWORD dwPad;
HANDLE EnumHandle;
HINSTANCE hProviderDll;
PF_NPEnumResource pfEnumResource;
PF_NPCloseEnum pfCloseEnum;
}
ENUM_HANDLE, *LPENUM_HANDLE;
typedef struct _REMEMBER_HANDLE
{
DWORD Key;
DWORD dwPad;
HKEY ConnectKey;
DWORD KeyIndex;
DWORD ConnectionType;
}
REMEMBER_HANDLE, *LPREMEMBER_HANDLE;
//
// CONSTANTS
//
#define DONE 1
#define MORE_ENTRIES 2
#define NOT_OPENED 3
#define CONNECT_TABLE_KEY 0x6e6e4f63 // "cOnn"
#define STATE_TABLE_KEY 0x74417473 // "stAt"
#define PROVIDER_ENUM_KEY 0x764f7270 // "prOv"
#define REMEMBER_KEY 0x626D4572 // "rEmb"
#define REGSTR_PATH_NETWORK_POLICIES \
REGSTR_PATH_POLICIES L"\\" REGSTR_KEY_NETWORK
//
// Macros for rounding a value up/down to a WCHAR boundary.
// Note: These macros assume that sizeof(WCHAR) is a power of 2.
//
#define ROUND_DOWN(x) ((x) & ~(sizeof(WCHAR) - 1))
#define ROUND_UP(x) (((x) + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1))
//
// LOCAL FUNCTION PROTOTYPES
//
DWORD
MprCopyProviderEnum(
IN LPNETRESOURCEW ProviderBuffer,
IN OUT LPDWORD EntryCount,
IN OUT LPBYTE *TempBufPtr,
IN OUT LPDWORD BytesLeft
);
DWORD
MprCopyResource(
IN OUT LPBYTE *BufPtr,
IN const NETRESOURCEW *Resource,
IN OUT LPDWORD BytesLeft
);
DWORD
MprEnumNetwork(
IN OUT LPNETWORK_HEADER StateTable,
IN OUT LPDWORD NumEntries,
IN OUT LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
);
DWORD
MprEnumConnect(
IN OUT LPCONNECT_HEADER ConnectEnumHeader,
IN OUT LPDWORD NumEntries,
IN OUT LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
);
DWORD
MprOpenEnumNetwork(
OUT LPHANDLE lphEnum
);
DWORD
MprOpenEnumConnect(
IN DWORD dwScope,
IN DWORD dwType,
IN DWORD dwUsage,
IN LPNETRESOURCE lpNetResource,
OUT LPHANDLE lphEnum
);
DWORD
MprProviderEnum(
IN LPENUM_HANDLE EnumHandlePtr,
IN OUT LPDWORD lpcCount,
IN LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
);
DWORD
MprOpenRemember(
IN DWORD dwType,
OUT LPHANDLE lphRemember
);
DWORD
MprEnumRemembered(
IN OUT LPREMEMBER_HANDLE RememberInfo,
IN OUT LPDWORD NumEntries,
IN OUT LPBYTE lpBuffer,
IN OUT LPDWORD lpBufferSize
);
DWORD
MprMultiStrBuffSize(
IN LPTSTR lpString1,
IN LPTSTR lpString2,
IN LPTSTR lpString3,
IN LPTSTR lpString4,
IN LPTSTR lpString5
) ;
class CProviderOpenEnum : public CRoutedOperation
{
public:
CProviderOpenEnum(
DWORD dwScope,
DWORD dwType,
DWORD dwUsage,
LPNETRESOURCEW lpNetResource,
LPHANDLE lphEnum
) :
CRoutedOperation(DBGPARM("ProviderOpenEnum")
PROVIDERFUNC(OpenEnum)),
_dwScope (dwScope ),
_dwType (dwType ),
_dwUsage (dwUsage ),
_lpNetResource(lpNetResource),
_lphEnum (lphEnum )
{ }
protected:
DWORD GetResult(); // overrides CRoutedOperation implementation
private:
DWORD _dwScope;
DWORD _dwType;
DWORD _dwUsage;
LPNETRESOURCEW _lpNetResource;
LPHANDLE _lphEnum;
HANDLE _ProviderEnumHandle; // Enum handle returned by provider
DECLARE_CROUTED
};
DWORD
WNetOpenEnumW (
IN DWORD dwScope,
IN DWORD dwType,
IN DWORD dwUsage,
IN LPNETRESOURCEW lpNetResource,
OUT LPHANDLE lphEnum
)
/*++
Routine Description:
This API is used to open an enumeration of network resources or existing
connections. It must be called to obtain a valid handle for enumeration.
NOTE:
For GlobalNet Enum, the caller must get a new handle for each level that
is desired. For the other scopes, the caller gets a single handle and
with that can enumerate all resources.
Arguments:
dwScope - Determines the scope of the enumeration. This can be one of:
RESOURCE_CONNECTED - All Currently connected resources.
RESOURCE_GLOBALNET - All resources on the network.
RESOURCE_REMEMBERED - All persistent connections.
RESOURCE_RECENT - Same as RESOURCE_REMEMBERED (supported for Win95
semi-compatibility)
RESOURCE_CONTEXT - The resources associated with the user's current
and default network context (as defined by the providers).
RESOURCE_SHAREABLE - All shareable resources on the given server
dwType - Used to specify the type of resources on interest. This is a
bitmask which may be any combination of:
RESOURCETYPE_DISK - All disk resources
RESOURCETYPE_PRINT - All print resources
If this is 0. all types of resources are returned. If a provider does
not have the capability to distinguish between print and disk
resources at a level, it may return all resources.
dwUsage - Used to specify the usage of resources of interest. This is a
bitmask which may be any combination of:
RESOURCEUSAGE_CONNECTABLE - all connectable resources.
RESOURCEUSAGE_CONTAINER - all container resources.
The bitmask may be 0 to match all.
lpNetResource - This specifies the container to perform the enumeration.
If it is NULL, the logical root of the network is assumed, and the
router is responsible for obtaining the information for return.
lphEnum - If the Open was successful, this will contain a handle that
can be used for future calls to WNetEnumResource.
Return Value:
WN_SUCCESS - Indicates the operation was successful.
WN_NOT_CONTAINER - Indicates that lpNetResource does not point to a
container.
WN_BAD_VALUE - Invalid dwScope or dwType, or bad combination of parameters
is specified.
WN_NO_NETWORK - network is not present.
--*/
{
DWORD status = WN_SUCCESS;
//
// dwScope MUST be set to either GLOBALNET or CONNECTED or REMEMBERED
// or RECENT or CONTEXT or SHAREABLE.
// This is verified in the switch statement below.
//
//
// dwType is a bit mask that can have any combination of the DISK
// or PRINT bits set. Or it can be the value 0.
//
if (dwType & ~(RESOURCETYPE_DISK | RESOURCETYPE_PRINT)) {
status = WN_BAD_VALUE;
goto CleanExit;
}
//
// dwUsage is a bit mask that can have any combination of the CONNECTABLE
// or CONTAINER bits set. Or it can be the value 0. This field is
// ignored if dwScope is not RESOURCE_GLOBALNET.
//
if (dwScope == RESOURCE_GLOBALNET) {
if (dwUsage & ~(RESOURCEUSAGE_ALL)) {
status = WN_BAD_VALUE;
goto CleanExit;
}
}
//
// Make sure the user passed in a valid OUT parameter
//
__try
{
PROBE_FOR_WRITE((LPDWORD)lphEnum);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
status = WN_BAD_POINTER;
}
if (status != WN_SUCCESS)
{
goto CleanExit;
}
//
// Check to see if it is a top-level enum request.
//
if (lpNetResource == NULL
||
(IS_EMPTY_STRING(lpNetResource->lpProvider) &&
IS_EMPTY_STRING(lpNetResource->lpRemoteName))
||
dwScope == RESOURCE_SHAREABLE)
{
//
// lpNetResource is NULL or represents no resource or this is
// a request for shareable resources.
// This is a top-level enum request, therefore, the MPR must provide
// the information.
//
switch(dwScope) {
case RESOURCE_CONNECTED:
case RESOURCE_CONTEXT:
case RESOURCE_SHAREABLE:
{
MprCheckProviders();
CProviderSharedLock PLock;
INIT_IF_NECESSARY(NETWORK_LEVEL,status);
if (MprNetIsAvailable()) {
status = MprOpenEnumConnect(dwScope,
dwType,
dwUsage,
lpNetResource,
lphEnum);
}
else
status = WN_NO_NETWORK ;
break;
}
case RESOURCE_GLOBALNET:
{
MprCheckProviders();
CProviderSharedLock PLock;
INIT_IF_NECESSARY(NETWORK_LEVEL,status);
if (MprNetIsAvailable()) {
status = MprOpenEnumNetwork(lphEnum);
}
else
status = WN_NO_NETWORK ;
break;
}
case RESOURCE_REMEMBERED:
case RESOURCE_RECENT:
MPR_LOG(TRACE,"OpenEnum RESOURCE_REMEMBERED\n",0);
status = MprOpenRemember(dwType, lphEnum);
break;
default:
status = WN_BAD_VALUE;
break;
}
}
else {
//
// Request is for one of the providers. It should be for a
// GLOBALNET enumeration. It is not allowed to request any
// other type of enumeration with a pointer to a resource
// buffer.
//
if (dwScope != RESOURCE_GLOBALNET) {
status = WN_BAD_VALUE;
goto CleanExit;
}
CProviderOpenEnum ProviderOpenEnum(
dwScope,
dwType,
dwUsage,
lpNetResource,
lphEnum);
status = ProviderOpenEnum.Perform(TRUE);
}
CleanExit:
if (status != WN_SUCCESS) {
SetLastError(status);
}
return(status);
}
DWORD
WNetEnumResourceW (
IN HANDLE hEnum,
IN OUT LPDWORD lpcCount,
OUT LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
)
/*++
Routine Description:
This function is used to obtain an array of NETRESOURCE structures each
of which describes a network resource.
Arguments:
hEnum - This is a handle that was obtained from an WNetOpenEnum call.
lpcCount - Specifies the number of entries requested. -1 indicates
as many entries as possible are requested. If the operation is
successful, this location will receive the number of entries
actually read.
lpBuffer - A pointer to the buffer to receive the enumeration result,
which are returned as an array of NETRESOURCE entries. The buffer
is valid until the next call using hEnum.
lpBufferSize - This specifies the size of the buffer passed to the function
call. It will contain the required buffer size if WN_MORE_DATA is
returned.
Return Value:
WN_SUCCESS - Indicates that the call is successful, and that the caller
should continue to call WNetEnumResource to continue the enumeration.
WN_NO_MORE_ENTRIES - Indicates that the enumeration completed successfully.
The following return codes indicate an error occured and GetLastError
may be used to obtain another copy of the error code:
WN_MORE_DATA - Indicates that the buffer is too small for even one
entry.
WN_BAD_HANDLE - hEnum is not a valid handle.
WN_NO_NETWORK - The Network is not present. This condition is checked
for before hEnum is tested for validity.
History:
12-Feb-1992 Johnl Removed requirement that buffersize must be at
least as large as NETRESOURCEW (bug 5790)
--*/
{
DWORD status = WN_SUCCESS;
//
// Screen the parameters as best we can.
//
//
// Probe the handle
//
__try {
*(volatile DWORD *)hEnum;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
MPR_LOG(ERROR,"WNetEnumResource:Unexpected Exception 0x%lx\n",status);
}
status = WN_BAD_HANDLE;
}
__try {
PROBE_FOR_WRITE(lpcCount);
if (IS_BAD_BYTE_BUFFER(lpBuffer, lpBufferSize)) {
status = WN_BAD_POINTER;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
MPR_LOG(ERROR,"WNetEnumResource:Unexpected Exception 0x%lx\n",status);
}
status = WN_BAD_POINTER;
}
if (status != WN_SUCCESS) {
goto CleanExit;
}
switch(*(LPDWORD)hEnum){
case CONNECT_TABLE_KEY:
//
// Call on Providers to enumerate connections.
//
status = MprEnumConnect(
(LPCONNECT_HEADER)hEnum, // key is part of structure
lpcCount,
lpBuffer,
lpBufferSize);
break;
case STATE_TABLE_KEY:
//
// Enumerate the top level NetResource structure maintained by
// the router.
//
status = MprEnumNetwork(
(LPNETWORK_HEADER)hEnum,
lpcCount,
lpBuffer,
lpBufferSize);
break;
case PROVIDER_ENUM_KEY:
//
// Call on providers to enumerate resources on the network.
//
status = MprProviderEnum(
(LPENUM_HANDLE)hEnum, // key is part of structure
lpcCount,
lpBuffer,
lpBufferSize);
break;
case REMEMBER_KEY:
//
// Enumerate the connections in the current user section of the
// registry.
//
status = MprEnumRemembered(
(LPREMEMBER_HANDLE)hEnum,
lpcCount,
(LPBYTE)lpBuffer,
lpBufferSize);
break;
default:
status = WN_BAD_HANDLE;
}
CleanExit:
if(status != WN_SUCCESS) {
SetLastError(status);
}
return(status);
}
DWORD
WNetCloseEnum (
IN HANDLE hEnum
)
/*++
Routine Description:
Closes an enumeration handle that is owned by the router.
In cases where the router is acting as a proxy for a single provider,
an attempt is made to return any error information from this provider
back to the user. This makes the router as transparent as possible.
Arguments:
hEnum - This must be a handle obtained from a call to WNetOpenEnum.
Return Value:
WN_SUCCESS - The operation was successful.
WN_NO_NETWORK - The Network is not present. This condition is checked
before hEnum is tested for validity.
WN_BAD_HANDLE - hEnum is not a valid handle.
--*/
{
DWORD status = WN_SUCCESS;
DWORD i;
//
// Probe the handle
//
__try {
*(volatile DWORD *)hEnum;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
MPR_LOG(ERROR,"WNetCloseEnum:Unexpected Exception 0x%lx\n",status);
}
status = WN_BAD_HANDLE;
}
if (status != WN_SUCCESS) {
SetLastError(WN_BAD_HANDLE);
return(status);
}
//
// Use hEnum as a pointer and check the DWORD value at its location.
// If it contains a CONNECT_TABLE_KEY, we must close all handles to
// the providers before freeing the memory for the table.
// If it is a STATE_TABLE_KEY, we just free the memory.
//
switch(*(LPDWORD)hEnum){
case CONNECT_TABLE_KEY:
{
LPCONNECT_HEADER connectEnumHeader;
LPCONNECT_ENUM connectEnumTable;
connectEnumHeader = (LPCONNECT_HEADER)hEnum;
connectEnumTable = (LPCONNECT_ENUM)(connectEnumHeader + 1);
//
// Close all the open provider handles
//
MPR_LOG(TRACE,"Closing Connection Enum Handles from Providers\n",0);
for(i = 0; i < connectEnumHeader->dwNumProviders; i++) {
if((connectEnumTable[i].State != NOT_OPENED) &&
(connectEnumTable[i].pfCloseEnum != NULL))
{
status = connectEnumTable[i].pfCloseEnum(
connectEnumTable[i].ProviderEnumHandle);
if(status != WN_SUCCESS) {
//
// Because we are closing many handles at once, the failure
// is noted for debug purposes only.
//
MPR_LOG(ERROR,"WNetCloseEnum:(connect-provider #%d) failed\n",i);
MPR_LOG(ERROR,"WNetCloseEnum: error code = %d\n",status);
//
// Error information is returned if there is only one
// provider.
//
if (connectEnumHeader->dwNumProviders != 1) {
status = WN_SUCCESS;
}
}
//
// If this fires, the logic in MprOpenEnumConnect is wrong
//
ASSERT(connectEnumTable[i].hProviderDll != NULL);
FreeLibrary(connectEnumTable[i].hProviderDll);
}
else
{
//
// If this fires, the logic in MprOpenEnumConnect is wrong
// and we're not releasing a refcounted provider DLL
//
ASSERT(connectEnumTable[i].hProviderDll == NULL);
}
}
//
// Free the Table Memory
//
if (LocalFree(hEnum)) {
MPR_LOG(ERROR,"WNetCloseEnum:LocalFree(connect) failed %d\n",
GetLastError());
}
if (status != WN_SUCCESS)
{
SetLastError(status);
}
return(status);
}
case STATE_TABLE_KEY:
{
LPNETWORK_HEADER StateTableHeader;
LPNETWORK_ENUM StateTable;
//
// Free the State Table Memory.
//
MPR_LOG(TRACE,"Free State Table for Network Enum\n",0);
StateTableHeader = (LPNETWORK_HEADER)hEnum;
StateTable = (LPNETWORK_ENUM)(StateTableHeader + 1);
for (i = 0; i < StateTableHeader->dwNumProviders; i++)
{
if (StateTable[i].hProviderDll != NULL)
{
FreeLibrary(StateTable[i].hProviderDll);
LocalFree(StateTable[i].lpnr);
}
else
{
//
// If this fires, MprOpenEnumNetwork is causing a mem leak
//
ASSERT(StateTable[i].lpnr == NULL);
}
}
if (LocalFree(hEnum)) {
MPR_LOG(ERROR,"WNetCloseEnum:LocalFree(network) failed %d\n",
GetLastError());
}
return(WN_SUCCESS);
}
case PROVIDER_ENUM_KEY:
{
LPENUM_HANDLE enumHandle;
//
// Close the providers enumeration handle, and free the
// ENUM_HANDLE structure.
//
MPR_LOG(TRACE,"Closing Provider Enum Handle\n",0);
enumHandle = (LPENUM_HANDLE)hEnum;
ASSERT(enumHandle->pfCloseEnum != NULL);
status = (enumHandle->pfCloseEnum)(enumHandle->EnumHandle);
ASSERT(enumHandle->hProviderDll != NULL);
FreeLibrary(enumHandle->hProviderDll);
if (LocalFree(enumHandle) != 0) {
MPR_LOG(ERROR,"WNetCloseEnum:LocalFree(provider) failed %d\n",
GetLastError());
}
//
// Check the status returned from the Provider's CloseEnum
//
if(status != WN_SUCCESS) {
MPR_LOG(ERROR,"WNetCloseEnum:(provider) failed %d\n",status);
SetLastError(status);
}
return(status);
}
case REMEMBER_KEY:
{
LPREMEMBER_HANDLE rememberHandle;
rememberHandle = (LPREMEMBER_HANDLE)hEnum;
//
// Close the RegistryKey Handle associated with this handle.
//
if (rememberHandle->ConnectKey != NULL) {
RegCloseKey(rememberHandle->ConnectKey);
}
//
// Free up the memory for the handle.
//
if (LocalFree(rememberHandle) != 0) {
MPR_LOG(ERROR,"WNetCloseEnum:LocalFree(remember) failed %d\n",
GetLastError());
}
return(WN_SUCCESS);
}
default:
SetLastError(WN_BAD_HANDLE);
return(WN_BAD_HANDLE);
}
}
DWORD
MprOpenEnumConnect(
IN DWORD dwScope,
IN DWORD dwType,
IN DWORD dwUsage,
IN LPNETRESOURCE lpNetResource,
OUT LPHANDLE lphEnum
)
/*++
Routine Description:
This function handles the opening of connection enumerations and context
enumerations. It does this by sending an OpenEnum to all Providers, and
storing the returned handles in a table. The handle that is returned is
a pointer to this table.
The first DWORD in the table is a key that will help to identify a
correct table. The second DWORD is a Boolean value that tells whether
a NETRESOURCE structure representing the root of the network needs to be
returned in the enumeration.
Arguments:
dwScope - RESOURCE_CONNECTED, RESOURCE_CONTEXT, or RESOURCE_SHAREABLE
dwType -
dwUsage -
lpNetResource -
lphEnum - This is a pointer to a location where the handle for
the connection enumeration is to be stored.
Return Value:
WN_SUCCESS - The operation was successful.
WN_OUT_OF_MEMORY - The memory allocation for the handle was unsuccessful.
--*/
{
DWORD i;
DWORD status;
LPCONNECT_HEADER connectEnumHeader;
LPCONNECT_ENUM connectEnumTable;
LPPROVIDER provider;
BOOL fcnSupported = FALSE; // Is fcn supported by a provider?
BOOL atLeastOne=FALSE;
BOOL bDynamicEntries = TRUE; // Whether to show dynamic entries
// in the net neighborhood
HKEY hkPolicies = NULL;
ASSERT(dwScope == RESOURCE_CONNECTED
||
dwScope == RESOURCE_CONTEXT
||
dwScope == RESOURCE_SHAREABLE);
ASSERT_INITIALIZED(NETWORK);
//
// If there are no providers, return NO_NETWORK
//
if (GlobalNumActiveProviders == 0) {
return(WN_NO_NETWORK);
}
//
// Allocate the handle table with enough room for a header.
//
connectEnumHeader = (LPCONNECT_HEADER) LocalAlloc(
LPTR,
sizeof(CONNECT_HEADER) +
sizeof(CONNECT_ENUM) * GlobalNumProviders
);
if (connectEnumHeader == NULL) {
MPR_LOG(ERROR,"MprOpenEnumConnect:LocalAlloc Failed %d\n",GetLastError());
return(WN_OUT_OF_MEMORY);
}
//
// Initialize the key used in the connect table.
//
connectEnumHeader->Key = CONNECT_TABLE_KEY;
connectEnumHeader->ReturnRoot = FALSE;
connectEnumHeader->dwNumProviders = GlobalNumProviders;
connectEnumHeader->dwNumActiveProviders = GlobalNumActiveProviders;
connectEnumTable = (LPCONNECT_ENUM)(connectEnumHeader + 1);
//
// Check the policy on whether dynamic entries are to be shown in the
// network neighborhood. By default, they are shown.
//
if (dwScope == RESOURCE_CONTEXT)
{
if (MprOpenKey(
HKEY_CURRENT_USER,
REGSTR_PATH_NETWORK_POLICIES,
&hkPolicies,
KEY_READ))
{
bDynamicEntries = ! (MprGetKeyNumberValue(
hkPolicies,
REGSTR_VAL_NOWORKGROUPCONTENTS,
FALSE));
}
else
{
hkPolicies = NULL;
}
}
//
// Initialize all state flags for providers to the NOT_OPENED state, so
// we won't try to enumerate or close their handles unless we actually
// got handles from them.
// Initialize handles for the network providers by calling them with
// OpenEnum.
//
for(i=0; i<GlobalNumProviders; i++) {
connectEnumTable[i].State = NOT_OPENED;
provider = GlobalProviderInfo + i;
if ((provider->InitClass & NETWORK_TYPE) &&
(provider->OpenEnum != NULL)) {
if (dwScope == RESOURCE_CONTEXT)
{
DWORD dwCaps = provider->GetCaps(WNNC_ENUMERATION);
if (dwCaps & WNNC_ENUM_GLOBAL)
{
// A browsing network is present, so show root,
// even if network is down, and even if ENUM_CONTEXT
// isn't supported.
connectEnumHeader->ReturnRoot = TRUE;
}
if ((dwCaps & WNNC_ENUM_CONTEXT) == 0)
{
// This provider can't show hood entries, so skip it.
continue;
}
}
else if (dwScope == RESOURCE_SHAREABLE)
{
DWORD dwCaps = provider->GetCaps(WNNC_ENUMERATION);
if ((dwCaps & WNNC_ENUM_SHAREABLE) == 0)
{
// This provider can't show shareable resources, so skip it.
continue;
}
}
fcnSupported = TRUE;
if (bDynamicEntries)
{
//
// Refcount the provider
//
connectEnumTable[i].hProviderDll = LoadLibraryEx(provider->DllName,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
if (connectEnumTable[i].hProviderDll == NULL)
{
status = GetLastError();
//
// This can happen under extreme low memory conditions. The
// loader can sometimes return ERROR_MOD_NOT_FOUND in this case.
//
MPR_LOG2(ERROR,
"MprOpenEnumConnect: LoadLibraryEx on %ws FAILED %d\n",
provider->DllName,
status);
ASSERT(status == ERROR_NOT_ENOUGH_MEMORY || status == ERROR_MOD_NOT_FOUND);
continue;
}
connectEnumTable[i].pfEnumResource = provider->EnumResource;
connectEnumTable[i].pfCloseEnum = provider->CloseEnum;
status = provider->OpenEnum(
dwScope, // Scope
dwType, // Type
dwUsage, // Usage
(dwScope == RESOURCE_SHAREABLE ? lpNetResource : NULL), // NetResource
&(connectEnumTable[i].ProviderEnumHandle)); // hEnum
if (status != WN_SUCCESS) {
MPR_LOG(ERROR,"MprOpenEnumConnect:OpenEnum Failed %d\n",status);
MPR_LOG(ERROR,
"That was for the %ws Provider\n",
provider->Resource.lpProvider);
FreeLibrary(connectEnumTable[i].hProviderDll);
connectEnumTable[i].hProviderDll = NULL;
connectEnumTable[i].pfEnumResource = NULL;
connectEnumTable[i].pfCloseEnum = NULL;
}
else {
//
// At least one provider has returned a handle.
//
atLeastOne = TRUE;
//
// Set the state to MORE_ENTRIES, so we later enumerate from the
// handle and/or close it.
//
connectEnumTable[i].State = MORE_ENTRIES;
MPR_LOG(TRACE,"MprOpenEnumConnect: OpenEnum Handle = 0x%lx\n",
connectEnumTable[i].ProviderEnumHandle);
}
}
else
{
// Succeed the WNetOpenEnum but leave this provider as NOT_OPENED
atLeastOne = TRUE;
}
}
}
if (connectEnumHeader->ReturnRoot)
{
// Able to show the root object. Check the policy on whether
// to show it.
connectEnumHeader->ReturnRoot = ! (MprGetKeyNumberValue(
hkPolicies,
REGSTR_VAL_NOENTIRENETWORK,
FALSE));
fcnSupported = TRUE;
atLeastOne = TRUE;
}
if (hkPolicies)
{
RegCloseKey(hkPolicies);
}
if (fcnSupported == FALSE) {
//
// No providers in the list support the API function. Therefore,
// we assume that no networks are installed.
// Note that in this case, atLeastOne will always be FALSE.
//
status = WN_NOT_SUPPORTED;
}
//
// return the handle (pointer to connectEnumTable);
//
*lphEnum = connectEnumHeader;
if (atLeastOne == FALSE) {
//
// If none of the providers returned a handle, then return the
// status from the last provider.
//
*lphEnum = NULL;
LocalFree( connectEnumHeader);
return(status);
}
return(WN_SUCCESS);
}
DWORD
MprOpenEnumNetwork(
OUT LPHANDLE lphEnum
)
/*++
Routine Description:
This function handles the opening of net resource enumerations.
It does this by allocating a table of Provider State Flags and returning
a handle to that table. The state flags (or for each provider) will
be set to MORE_ENTRIES. Later, when enumerations take place, the state
for each provider is changed to DONE after the buffer is successfully
loaded with the the NETRESOURCE info for that provider.
Arguments:
lphEnum - This is a pointer to a location where the handle for
the network resource enumeration is to be stored.
Return Value:
WN_SUCCESS - The operation was successful.
WN_OUT_OF_MEMORY - The memory allocation for the handle was unsuccessful.
--*/
{
LPNETWORK_ENUM stateTable;
LPNETWORK_HEADER stateTableHeader;
DWORD i;
ASSERT_INITIALIZED(NETWORK);
//
// If there are no providers, return NO_NETWORK
//
if (GlobalNumActiveProviders == 0) {
return(WN_NO_NETWORK);
}
//
// Allocate the state table.
//
stateTableHeader = (LPNETWORK_HEADER) LocalAlloc(
LPTR,
sizeof(NETWORK_HEADER) +
sizeof(NETWORK_ENUM) * GlobalNumProviders
);
if (stateTableHeader == NULL) {
MPR_LOG(ERROR,"MprOpenEnumNetwork:LocalAlloc Failed %d\n",GetLastError());
return(WN_OUT_OF_MEMORY);
}
stateTableHeader->Key = STATE_TABLE_KEY;
stateTableHeader->dwNumProviders = GlobalNumProviders;
stateTableHeader->dwNumActiveProviders = GlobalNumActiveProviders;
stateTable = (LPNETWORK_ENUM)(stateTableHeader + 1);
//
// Initialize state flags for all network providers to the MORE_ENTRIES state.
//
for(i = 0; i < GlobalNumProviders; i++) {
if (GlobalProviderInfo[i].InitClass & NETWORK_TYPE)
{
if (GlobalProviderInfo[i].Handle != NULL)
{
stateTable[i].hProviderDll = LoadLibraryEx(GlobalProviderInfo[i].DllName,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
if (stateTable[i].hProviderDll == NULL)
{
DWORD status = GetLastError();
//
// This can happen under extreme low memory conditions. The
// loader can sometimes return ERROR_MOD_NOT_FOUND in this case.
//
MPR_LOG1(ERROR,
"MprOpenEnumNetwork: LoadLibraryEx on %ws FAILED\n",
GlobalProviderInfo[i].DllName);
ASSERT(status == ERROR_NOT_ENOUGH_MEMORY || status == ERROR_MOD_NOT_FOUND);
}
else
{
LPBYTE lpTempBuffer;
DWORD dwSize = 0;
DWORD dwStatus;
//
// Figure out how much space we'll need for the NETRESOURCE.
// It needs to be copied since the provider (and its resource)
// may go away between now and the WNetEnumResource call.
//
dwStatus = MprCopyResource(NULL,
&GlobalProviderInfo[i].Resource,
&dwSize);
ASSERT(dwStatus == WN_MORE_DATA);
stateTable[i].lpnr = (LPNETRESOURCE)LocalAlloc(LPTR, dwSize);
if (stateTable[i].lpnr == NULL)
{
MPR_LOG0(ERROR, "MprOpenEnumNetwork: LocalAlloc FAILED\n");
//
// Rather than fail silently in this case, bail out
//
for (UINT j = 0; j <= i; j++)
{
if (stateTable[j].hProviderDll)
{
FreeLibrary(stateTable[j].hProviderDll);
stateTable[j].hProviderDll = NULL;
stateTable[j].State = MORE_ENTRIES;
}
LocalFree(stateTable[j].lpnr);
}
LocalFree(stateTableHeader);
return(WN_OUT_OF_MEMORY);
}
else
{
lpTempBuffer = (LPBYTE)stateTable[i].lpnr;
dwStatus = MprCopyResource(&lpTempBuffer,
&GlobalProviderInfo[i].Resource,
&dwSize);
ASSERT(dwStatus == WN_SUCCESS);
}
}
}
stateTable[i].State = MORE_ENTRIES;
}
else {
stateTable[i].State = DONE;
}
}
//
// return the handle (pointer to stateTable);
//
*lphEnum = stateTableHeader;
return(WN_SUCCESS);
}
DWORD
MprEnumConnect(
IN OUT LPCONNECT_HEADER ConnectEnumHeader,
IN OUT LPDWORD NumEntries,
IN OUT LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
)
/*++
Routine Description:
This function looks in the ConnectEnumTable for the next provider that
has MORE_ENTRIES. It begins requesting Enum Data from that provider -
each time copying data that is returned from the provider enum into the
users enum buffer (lpBuffer). This continues until we finish, or
we reach the requested number of elements, or the user buffer is full.
Each time we enumerate a provider to completion, that provider is
marked as DONE.
Note, for a context enumeration, the first NETRESOURCE returned in the
enumeration is a constant NETRESOURCE representing the root of the
network. This is done to make it easy for the shell to display a
"Rest of Network" object in a "Network Neighborhood" view.
Arguments:
ConnectEnumHeader - This is a pointer to a CONNECT_HEADER structure
followed by an array of CONNECT_ENUM structures. The ReturnRoot
member of the header tells whether the root object needs to be
returned at the start of the enumeration. On exit, if the root
object has been returned, the ReturnRoot member is set to FALSE.
NumEntries - On entry this points to the maximum number of entries
that the user desires to receive. On exit it points to the
number of entries that were placed in the users buffer.
lpBuffer - This is a pointer to the users buffer in which the
enumeration data is to be placed.
lpBufferSize - This is the size (in bytes) of the users buffer. It will
be set to the size of the required buffer size of WN_MORE_DATA is
returned.
Return Value:
WN_SUCCESS - This indicates that the call is returning some entries.
However, the enumeration is not complete due to one of the following:
1) There was not enough buffer space.
2) The requested number of entries was reached.
3) There is no more data to enumerate - the next call will
return WN_NO_MORE_ENTRIES.
WN_MORE_DATA - This indicates that the buffer was not large enough
to receive one enumeration entry.
WN_NO_MORE_ENTRIES - This indicates that there are no more entries
to enumerate. No data is returned with this return code.
WN_NO_NETWORK - If there are no providers loaded.
Note:
--*/
{
DWORD i; // provider index
DWORD status=WN_NO_MORE_ENTRIES;
DWORD entriesRead=0; // number of entries read into the buffer.
LPBYTE tempBufPtr; // pointer to top of remaining free buffer space.
LPNETRESOURCEW providerBuffer; // buffer for data returned from provider
DWORD bytesLeft; // Numer of bytes left in the buffer
DWORD entryCount; // number of entries read from provider
LPCONNECT_ENUM ConnectEnumTable = (LPCONNECT_ENUM) (ConnectEnumHeader + 1);
// Start of array
//
// If there are no providers, return NO_NETWORK
//
if (ConnectEnumHeader->dwNumActiveProviders == 0) {
return(WN_NO_NETWORK);
}
bytesLeft = ROUND_DOWN(*lpBufferSize);
tempBufPtr = (LPBYTE) lpBuffer;
//
// Check to see if there are any flags in state table that indicate
// MORE_ENTRIES. If not, we want to return WN_NO_MORE_ENTRIES.
//
for(i = 0; i < ConnectEnumHeader->dwNumProviders; i++) {
if(ConnectEnumTable[i].State == MORE_ENTRIES) {
break;
}
}
if ( (i == ConnectEnumHeader->dwNumProviders) && (! ConnectEnumHeader->ReturnRoot) ) {
*NumEntries = 0;
return(WN_NO_MORE_ENTRIES);
}
//
// If no entries are requested, we have nothing to do
//
if (*NumEntries == 0) {
return WN_SUCCESS;
}
//
// Allocate a buffer for the provider to return data in.
// The buffer size must equal the number of bytes left in the
// user buffer.
// (We can't have the provider write data directly to the caller's
// buffer because NPEnumResource is not required to place strings
// at the end of the buffer -- only at the end of the array of
// NETRESOURCEs.)
//
providerBuffer = (LPNETRESOURCEW) LocalAlloc(LPTR, bytesLeft);
if (providerBuffer == NULL) {
MPR_LOG(ERROR,"MprEnumConnect:LocalAlloc Failed %d\n",
GetLastError());
*NumEntries = 0;
return(WN_OUT_OF_MEMORY);
}
//
// Copy the root-of-network resource if required.
//
if (ConnectEnumHeader->ReturnRoot)
{
NETRESOURCEW RootResource = {
RESOURCE_GLOBALNET, // dwScope
RESOURCETYPE_ANY, // dwType
RESOURCEDISPLAYTYPE_ROOT, // dwDisplayType
RESOURCEUSAGE_CONTAINER, // dwUsage
NULL, // lpLocalName
NULL, // lpRemoteName
g_wszEntireNetwork, // lpComment
NULL // lpProvider
};
status = MprCopyResource(&tempBufPtr, &RootResource, &bytesLeft);
if (status == WN_SUCCESS)
{
entriesRead = 1;
ConnectEnumHeader->ReturnRoot = FALSE;
}
else
{
if (status == WN_MORE_DATA)
{
//
// Not even enough room for one NETRESOURCE
//
*lpBufferSize = bytesLeft;
}
goto CleanExit;
}
}
//
// Loop until we have copied from all Providers or until the
// the maximum number of entries has been reached.
//
for ( ; i < ConnectEnumHeader->dwNumProviders && entriesRead < *NumEntries; i++)
{
if (ConnectEnumTable[i].State != MORE_ENTRIES)
{
//
// Skip providers that don't have more entries
//
continue;
}
if (ConnectEnumTable[i].hProviderDll == NULL) {
//
// If the provider has not been initialized because it is
// not "ACTIVE", then skip it.
//
ConnectEnumTable[i].State = DONE;
status = WN_SUCCESS;
continue;
}
//
// Adjust the entry count for any entries that have been read
// so far.
//
entryCount = *NumEntries - entriesRead;
//
// Call the provider to get the enumerated data
//
status = ConnectEnumTable[i].pfEnumResource(
ConnectEnumTable[i].ProviderEnumHandle,
&entryCount,
providerBuffer,
&bytesLeft ); // (note, the provider updates
// bytesLeft only if it returns
// WN_MORE_DATA)
switch (status)
{
case WN_SUCCESS:
MPR_LOG(TRACE,"EnumResourceHandle = 0x%lx\n",
ConnectEnumTable[i].ProviderEnumHandle);
status = MprCopyProviderEnum(
providerBuffer,
&entryCount,
&tempBufPtr,
&bytesLeft);
entriesRead += entryCount;
if (status != WN_SUCCESS) {
//
// An internal error occured - for some reason the
// buffer space left in the user buffer was smaller
// than the buffer space that the provider filled in.
// The best we can do in this case is return what data
// we have. WARNING: the data that didn't make it
// will become lost since the provider thinks it
// enumerated successfully, but we couldn't do anything
// with it.
//
MPR_LOG(ERROR,
"MprEnumConnect:MprCopyProviderEnum Internal Error %d\n",
status);
if(entriesRead > 0) {
status = WN_SUCCESS;
}
goto CleanExit;
}
//
// We successfully placed all the received data from
// that provider into the user buffer. In this case,
// if we haven't reached the requested number of entries,
// we want to loop around and ask the same provider
// to enumerate more. This time the provider should
// indicate why it quit last time - either there are
// no more entries, or we ran out of buffer space.
//
break;
case WN_NO_MORE_ENTRIES:
//
// This Provider has completed its enumeration, mark it
// as done and increment to the next provider. We don't
// want to return NO_MORE_ENTRIES status. That should
// only be returned by the check at beginning or end
// of this function.
//
ConnectEnumTable[i].State = DONE;
status = WN_SUCCESS;
break;
case WN_MORE_DATA:
//
// This provider has more data, but there is not enough
// room left in the user buffer to place any more data
// from this provider. We don't want to go on to the
// next provider until we're finished with this one. So
// if we have any entries at all to return, we send back
// a SUCCESS status. Otherwise, the WN_MORE_DATA status
// is appropriate.
//
if(entriesRead > 0) {
status = WN_SUCCESS;
}
else
{
//
// If 0 entries were read, then the provider should
// have set bytesLeft with the required buffer size
//
*lpBufferSize = ROUND_UP(bytesLeft);
}
goto CleanExit;
break;
default:
//
// We received an unexpected error from the Provider Enum
// call.
//
MPR_LOG(ERROR,"MprEnumConnect:ProviderEnum Error %d\n",status);
if(entriesRead > 0) {
//
// If we have received data so far, ignore this error
// and move on to the next provider. This provider will
// be left in the MORE_ENTRIES state, so that on some other
// pass - when all other providers are done, this error
// will be returned.
//
status = WN_SUCCESS;
}
else{
//
// No entries have been read so far. We can return
// immediately with the error.
//
goto CleanExit;
}
} // end switch (status)
} // end for (each provider)
//
// If we looped through all providers and they are all DONE.
// If there were no connections, then return proper error code.
//
if ((entriesRead == 0) && (status == WN_SUCCESS)) {
status = WN_NO_MORE_ENTRIES;
}
CleanExit:
//
// Update the number of entries to be returned to user.
//
*NumEntries = entriesRead;
LocalFree(providerBuffer);
return(status);
}
DWORD
MprEnumNetwork(
IN OUT LPNETWORK_HEADER StateTableHeader,
IN OUT LPDWORD NumEntries,
IN OUT LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
)
/*++
Routine Description:
This function Looks in the state table for the next provider that has
MORE_ENTRIES. It begins by copying the NETRESOURCE info for that one.
This continues until we finish, or we reach the requested number of
elements, or the buffer is full. Each time we copy a complete structure,
we mark that provider as DONE.
Arguments:
StateTable - This is a pointer to the state table that is managed by
the handle used in this request. The state table is a table of
flags used to indicate the enumeration state for a given
provider. These flags are in the same order as the provider
information in the GlobalProviderInfo Array. The state can be
either DONE or MORE_ENTRIES.
NumEntries - On entry this points to the maximum number of entries
that the user desires to receive. On exit it points to the
number of entries that were placed in the users buffer.
lpBuffer - This is a pointer to the users buffer in which the
enumeration data is to be placed.
lpBufferSize - This is the size (in bytes) of the users buffer.
Return Value:
WN_SUCCESS - This indicates that the call is returning some entries.
However, the enumeration is not complete due to one of the following:
1) There was not enough buffer space.
2) The requested number of entries was reached.
3) There is no more data to enumerate - the next call will
return WN_NO_MORE_ENTRIES.
WN_MORE_DATA - This indicates that the buffer was not large enough
to receive one enumeration entry.
WN_NO_MORE_ENTRIES - This indicates that there are no more entries
to enumerate. No data is returned with this return code.
Note:
CAUTION: "DONE" entries may appear anywhere in the statetable.
You cannot always rely on the fact that all of the entries
after a MORE_ENTRIES entry are also in the MORE_ENTRIES state.
This is because a provider that would not pass back a handle
at open time will be marked as DONE so that it gets skipped
at Enum Time.
--*/
{
DWORD i;
DWORD status;
DWORD entriesRead=0; // number of entries read into the buffer.
LPBYTE tempBufPtr; // pointer to top of remaining free buffer space.
DWORD bytesLeft; // num bytes left in free buffer space
LPNETWORK_ENUM StateTable = (LPNETWORK_ENUM)(StateTableHeader + 1);
//
// If there are no providers, return NO_NETWORK
//
if (StateTableHeader->dwNumActiveProviders == 0) {
return(WN_NO_NETWORK);
}
bytesLeft = ROUND_DOWN(*lpBufferSize);
tempBufPtr = (LPBYTE) lpBuffer;
//
// Check to see if there are any flags in state table that indicate
// MORE_ENTRIES. If not, we want to return WN_NO_MORE_ENTRIES.
//
for(i = 0; i < StateTableHeader->dwNumProviders; i++) {
if(StateTable[i].State == MORE_ENTRIES) {
break;
}
}
if ( i >= StateTableHeader->dwNumProviders ) {
*NumEntries = 0;
return(WN_NO_MORE_ENTRIES);
}
//
// Loop until we have copied from all Providers or until the
// the maximum number of entries has been reached.
//
for(; (i < StateTableHeader->dwNumProviders) && (entriesRead < *NumEntries); i++)
{
if (StateTable[i].State == MORE_ENTRIES) {
if (StateTable[i].hProviderDll == NULL)
{
//
// If the provider is not ACTIVE, skip it.
//
StateTable[i].State = DONE;
}
else
{
status = MprCopyResource(
&tempBufPtr,
StateTable[i].lpnr,
&bytesLeft);
if (status == WN_SUCCESS)
{
StateTable[i].State = DONE;
entriesRead++;
}
else
{
//
// The buffer must be full - so exit.
// If no entries are being returned, we will indicate
// that the buffer was not large enough for even one entry.
//
*NumEntries = entriesRead;
if (entriesRead > 0) {
return(WN_SUCCESS);
}
else {
*lpBufferSize = ROUND_UP(bytesLeft);
return(WN_MORE_DATA);
}
}
}
} // EndIf (state == MORE_ENTRIES)
} // EndFor (each provider)
//
// Update the number of entries to be returned to user
//
*NumEntries = entriesRead;
return(WN_SUCCESS);
}
DWORD
MprProviderEnum(
IN LPENUM_HANDLE EnumHandlePtr,
IN OUT LPDWORD lpcCount,
IN LPVOID lpBuffer,
IN OUT LPDWORD lpBufferSize
)
/*++
Routine Description:
This function calls the provider (identified by the EnumHandlePtr)
with a WNetEnumResource request. Aside from the EnumHandlePtr, all the
rest of the parameters are simply passed thru to the provider.
Arguments:
EnumHandlePtr - This is a pointer to an ENUM_HANDLE structure which
contains a pointer to the provider structure and the handle for
that provider's enumeration.
lpcCount - A pointer to a value that on entry contains the requested
number of elements to enumerate. On exit this contains the
actual number of elements enumerated.
lpBuffer - A pointer to the users buffer that the enumeration data
is to be placed into.
lpBufferSize - The number of bytes of free space in the user's buffer.
Return Value:
This function can return any return code that WNetEnumResource()
can return.
--*/
{
DWORD status;
ASSERT(EnumHandlePtr->pfEnumResource != NULL);
//
// Call the provider listed in the ENUM_HANDLE structure and ask it
// to enumerate.
//
status = EnumHandlePtr->pfEnumResource(
EnumHandlePtr->EnumHandle,
lpcCount,
lpBuffer,
lpBufferSize);
if (status == WN_SUCCESS) {
MPR_LOG(TRACE,"EnumResourceHandle = 0x%lx\n",
EnumHandlePtr->EnumHandle);
}
return(status);
}
DWORD
MprCopyResource(
IN OUT LPBYTE *BufPtr,
IN const NETRESOURCEW *Resource,
IN OUT LPDWORD BytesLeft
)
/*++
Routine Description:
This function copies a single NETRESOURCE structure into a buffer.
The structure gets copied to the top of the buffer, and the strings
that the structure references are copied to the bottom of the
buffer. So any remaining free buffer space is left in the middle.
Upon successful return from this function, BufPtr will point to
the top of this remaining free space, and BytesLeft will be updated
to indicate how many bytes of free space are remaining.
If there is not enough room in the buffer to copy the Resource and its
strings, an error is returned, and BufPtr is not changed.
Arguments:
BufPtr - This is a pointer to a location that upon entry, contains a
pointer to the buffer that the copied data is to be placed into.
Upon exit, this pointer location points to the next free location
in the buffer.
Resource - This points to the Resource Structure that is to be copied
into the buffer.
BytesLeft - This points to a location to where a count of the remaining
free bytes in the buffer is stored. This is updated on exit to
indicate the adjusted number of free bytes left in the buffer.
If the buffer is not large enough, and WN_MORE_DATA is returned, then
the size of the buffer required to fit all the data is returned in
this field.
Return Value:
WN_SUCCESS - The operation was successful
WN_MORE_DATA - The buffer was not large enough to contain the
Resource structure an its accompanying strings.
Note:
History:
02-Apr-1992 JohnL
Changed error return code to WN_MORE_DATA, added code to set the
required buffer size if WN_MORE_DATA is returned.
--*/
{
LPTSTR startOfFreeBuf;
LPTSTR endOfFreeBuf;
LPNETRESOURCEW newResource;
//
// The buffer must be at least large enough to hold a resource structure.
//
if (*BytesLeft < sizeof(NETRESOURCEW)) {
*BytesLeft = MprMultiStrBuffSize( Resource->lpRemoteName,
Resource->lpLocalName,
Resource->lpComment,
Resource->lpProvider,
NULL ) + sizeof(NETRESOURCEW) ;
return(WN_MORE_DATA);
}
//
// Copy the Resource structure into the beginning of the buffer.
//
newResource = (LPNETRESOURCEW) *BufPtr;
memcpy(newResource, Resource, sizeof(NETRESOURCEW));
startOfFreeBuf = (LPTSTR)((PCHAR)newResource + sizeof(NETRESOURCEW));
endOfFreeBuf = (LPTSTR)((LPBYTE)newResource + *BytesLeft);
//
// If a REMOTE_NAME string is to be copied, copy that and update the
// pointer in the structure.
//
if (Resource->lpRemoteName != NULL) {
//
// If we must copy the remote name,
//
if (!NetpCopyStringToBuffer(
Resource->lpRemoteName, // pointer to string
wcslen(Resource->lpRemoteName), // num chars in string
startOfFreeBuf, // start of open space
&endOfFreeBuf, // end of open space
&newResource->lpRemoteName)) { // where string pointer goes
*BytesLeft = MprMultiStrBuffSize( Resource->lpRemoteName,
Resource->lpLocalName,
Resource->lpComment,
Resource->lpProvider,
NULL ) ;
goto ErrorMoreData ;
}
}
else{
newResource->lpRemoteName = NULL;
}
//
// If a LOCAL_NAME string is to be copied, copy that and update the
// pointer in the structure.
//
if( ((Resource->dwScope == RESOURCE_CONNECTED) ||
(Resource->dwScope == RESOURCE_REMEMBERED))
&&
(Resource->lpLocalName != NULL) ) {
//
// If we must copy the local name,
//
if (!NetpCopyStringToBuffer(
Resource->lpLocalName, // pointer to string
wcslen(Resource->lpLocalName), // num chars in string
startOfFreeBuf, // start of open space
&endOfFreeBuf, // end of open space
&newResource->lpLocalName)) // where string pointer goes
{
goto ErrorMoreData ;
}
}
else{
newResource->lpLocalName = NULL;
}
//
// If a COMMENT string is to be copied, copy that and update the
// pointer in the structure.
//
if (Resource->lpComment != NULL) {
//
// If we must copy the comment string,
//
if (!NetpCopyStringToBuffer(
Resource->lpComment, // pointer to string
wcslen(Resource->lpComment), // num chars in string
startOfFreeBuf, // start of open space
&endOfFreeBuf, // end of open space
&newResource->lpComment)) // where string pointer goes
{
goto ErrorMoreData ;
}
}
else{
newResource->lpComment = NULL;
}
//
// If a PROVIDER string is to be copied, copy that and update the
// pointer in the structure.
//
if (Resource->lpProvider != NULL) {
//
// If we must copy the provider name,
//
if (!NetpCopyStringToBuffer(
Resource->lpProvider, // pointer to string
wcslen(Resource->lpProvider), // num chars in string
startOfFreeBuf, // start of open space
&endOfFreeBuf, // end of open space
&newResource->lpProvider)) // where string pointer goes
{
goto ErrorMoreData ;
}
}
else{
newResource->lpProvider = NULL;
}
//
// Update the returned information
//
*BufPtr = (LPBYTE)startOfFreeBuf;
*BytesLeft = (DWORD) ((LPBYTE) endOfFreeBuf - (LPBYTE) startOfFreeBuf);
return (WN_SUCCESS);
//
// This is reached when we couldn't fill the buffer because the given
// buffer size is too small. We therefore need to set the required
// buffer size before returning.
ErrorMoreData:
*BytesLeft = MprMultiStrBuffSize( Resource->lpRemoteName,
Resource->lpLocalName,
Resource->lpComment,
Resource->lpProvider,
NULL ) + sizeof(NETRESOURCEW) ;
return (WN_MORE_DATA);
}
DWORD
MprCopyProviderEnum(
IN LPNETRESOURCEW ProviderBuffer,
IN OUT LPDWORD EntryCount,
IN OUT LPBYTE *TempBufPtr,
IN OUT LPDWORD BytesLeft
)
/*++
Routine Description:
This function moves the enumerated NETRESOURCE structures that are
returned from a provider to a buffer that can be returned to the user.
The buffer that is returned to the user may contain enum data from
several providers. Because, we don't know how strings are packed in
the buffer that is returned from the provider, we must simply walk
through each structure and copy the information into the user buffer
in a format that we do know about. Then the amount of free space
left in the user buffer can be determined so that enum data from
another provider can be added to it.
Arguments:
ProviderBuffer - This is a pointer to the top of an array of NETRESOURCE
structures that is returned from one of the providers.
EntryCount - This points to the number of elements in the array that
was returned from the provider. On exit, this points to the number
of elements that was successfully copied. This should always be
the same as the number of elements passed in.
TempBufPtr - This is a pointer to the top of the free space in the user
buffer.
BytesLeft - Upon entry, this contains the number of free space bytes
in the user buffer. Upon exit, this contains the updated number
of free space bytes in the user buffer.
Return Value:
WN_SUCCESS - The operation was successful
WN_OUT_OF_MEMORY - The buffer was not large enough to contain all of
data the provider returned. This should never happen.
Note:
--*/
{
DWORD i;
DWORD status;
DWORD entriesRead=0;
//
// Loop for each element in the array of NetResource Structures.
//
for(i=0; i<*EntryCount; i++,ProviderBuffer++) {
status = MprCopyResource(
TempBufPtr,
ProviderBuffer,
BytesLeft);
if (status != WN_SUCCESS) {
MPR_LOG(ERROR,"MprCopyProviderEnum: Buffer Size Mismatch\n",0);
//
// The buffer must be full - this should never happen since
// the amount of data placed in the ProviderBuffer is limited
// by the number of bytes left in the user buffer.
//
ASSERT(0);
*EntryCount = entriesRead;
return(status);
}
entriesRead++;
}
*EntryCount = entriesRead;
return(WN_SUCCESS);
}
//===================================================================
// CProviderOpenEnum - open an enumeration by a provider
//===================================================================
DWORD
CProviderOpenEnum::ValidateRoutedParameters(
LPCWSTR * ppProviderName,
LPCWSTR * ppRemoteName,
LPCWSTR * ppLocalName
)
{
//
// Let the base class validate any specified provider name.
// Note: This must be done before setting _lpNetResource to NULL!
//
*ppProviderName = _lpNetResource->lpProvider;
//
// Check to see if a top level enumeration for the provider is requested.
// (This is different from a top level MPR enumeration.)
// A top level enum is signified by a net resource with either a special
// bit set in the dwUsage field, or a provider name but no remote name.
//
if ((_lpNetResource->dwUsage & RESOURCEUSAGE_RESERVED) ||
IS_EMPTY_STRING(_lpNetResource->lpRemoteName))
{
//
// Top level enum. Don't pass the net resource to the provider.
//
ASSERT(! IS_EMPTY_STRING(_lpNetResource->lpProvider));
_lpNetResource = NULL;
}
else
{
//
// Use the remote name as a hint to pick the provider order.
//
*ppRemoteName = _lpNetResource->lpRemoteName;
}
*ppLocalName = NULL;
return WN_SUCCESS;
}
DWORD
CProviderOpenEnum::TestProvider(
const PROVIDER * pProvider
)
{
ASSERT(MPR_IS_INITIALIZED(NETWORK));
if ((pProvider->GetCaps(WNNC_ENUMERATION) & WNNC_ENUM_GLOBAL) == 0)
{
return WN_NOT_SUPPORTED;
}
return ( pProvider->OpenEnum(
_dwScope,
_dwType,
_dwUsage,
_lpNetResource,
&_ProviderEnumHandle) );
}
DWORD
CProviderOpenEnum::GetResult()
{
//
// Let the base class try the providers until one responds
// CRoutedOperation::GetResult calls INIT_IF_NECESSARY
//
DWORD status = CRoutedOperation::GetResult();
if (status != WN_SUCCESS)
{
return status;
}
MPR_LOG(TRACE,"CProviderOpenEnum: OpenEnum Handle = 0x%lx\n",
_ProviderEnumHandle);
//
// Allocate memory to store the handle.
//
LPENUM_HANDLE enumHandleStruct =
(ENUM_HANDLE *) LocalAlloc(LPTR, sizeof(ENUM_HANDLE));
if (enumHandleStruct == NULL)
{
//
// If we can't allocate memory to store the handle
// away, then we must close it, and change the status
// to indicate a memory failure.
//
MPR_LOG(ERROR,"CProviderOpenEnum: LocalAlloc failed %d\n",
GetLastError());
LastProvider()->CloseEnum(_ProviderEnumHandle);
status = WN_OUT_OF_MEMORY;
}
else
{
//
// Store the handle in the ENUM_HANDLE structure and
// return the pointer to that structure as a handle
// for the user.
//
enumHandleStruct->Key = PROVIDER_ENUM_KEY;
//
// Refcount the provider
//
enumHandleStruct->hProviderDll = LoadLibraryEx(LastProvider()->DllName,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
if (enumHandleStruct->hProviderDll == NULL)
{
status = GetLastError();
//
// This can happen under extreme low memory conditions. The
// loader can sometimes return ERROR_MOD_NOT_FOUND in this case.
//
MPR_LOG2(ERROR,
"MprOpenEnumConnect: LoadLibraryEx on %ws FAILED %d\n",
LastProvider()->DllName,
status);
ASSERT(status == ERROR_NOT_ENOUGH_MEMORY || status == ERROR_MOD_NOT_FOUND);
LastProvider()->CloseEnum(_ProviderEnumHandle);
LocalFree(enumHandleStruct);
}
else
{
enumHandleStruct->pfEnumResource = LastProvider()->EnumResource;
enumHandleStruct->pfCloseEnum = LastProvider()->CloseEnum;
enumHandleStruct->EnumHandle = _ProviderEnumHandle;
*_lphEnum = enumHandleStruct;
}
}
return status;
}
DWORD
MprOpenRemember(
IN DWORD dwType,
OUT LPHANDLE lphRemember
)
/*++
Routine Description:
Arguments:
Return Value:
Note:
--*/
{
LPREMEMBER_HANDLE rememberInfo;
rememberInfo = (REMEMBER_HANDLE *) LocalAlloc(LPTR, sizeof(REMEMBER_HANDLE));
if (rememberInfo == NULL) {
MPR_LOG(ERROR,"MprOpenRemember:LocalAlloc Failed %d\n",GetLastError());
return(WN_OUT_OF_MEMORY);
}
rememberInfo->Key = REMEMBER_KEY;
rememberInfo->KeyIndex = 0;
rememberInfo->ConnectionType = dwType;
//
// Open the key to the connection information in the current user
// section of the registry.
//
// NOTE: If this fails, we must assume that there is no connection
// information stored. This is not an error condition.
// In this case, we store a NULL for the handle so that we know
// the situation. Each time EnumResource is called, we can try
// to open the key again.
//
if (!MprOpenKey(
HKEY_CURRENT_USER,
CONNECTION_KEY_NAME,
&(rememberInfo->ConnectKey),
DA_READ)) {
MPR_LOG(ERROR,"MprOpenRemember: MprOpenKey Failed\n",0);
rememberInfo->ConnectKey = NULL;
}
*lphRemember = (HANDLE)rememberInfo;
return(WN_SUCCESS);
}
DWORD
MprEnumRemembered(
IN OUT LPREMEMBER_HANDLE RememberInfo,
IN OUT LPDWORD NumEntries,
IN OUT LPBYTE lpBuffer,
IN OUT LPDWORD lpBufferSize
)
/*++
Routine Description:
Arguments:
RememberInfo - This is a pointer to REMEMBER_HANDLE data structure
that contains the context information for this enumeration handle.
NumEntries - On entry this points to the maximum number of entries
that the user desires to receive. On exit it points to the
number of entries that were placed in the users buffer.
lpBuffer - This is a pointer to the users buffer in which the
enumeration data is to be placed.
lpBufferSize - This is the size (in bytes) of the users buffer.
Return Value:
WN_SUCCESS - The call was successful, and some entries were returned.
However, there are still more entries to be enumerated.
WN_NO_MORE_ENTRIES - This function has no data to return because
there was no further connection information in the registry.
WN_CONNOT_OPEN_PROFILE - This function could open a key to the
connection information, but could not get any information about
that key.
WN_MORE_DATA - The caller's buffer was too small for even one entry.
Note:
History:
Changed to return "status" instead of WN_SUCCESS
--*/
{
DWORD status = WN_SUCCESS ;
LPTSTR userName;
NETRESOURCEW netResource;
LPBYTE tempBufPtr;
DWORD bytesLeft;
DWORD entriesRead = 0;
DWORD numSubKeys;
DWORD maxSubKeyLen;
DWORD maxValueLen;
if ((RememberInfo->ConnectKey == NULL) && (RememberInfo->KeyIndex == 0)) {
//
// If we failed to open the key at Open-time, attempt to open it
// now. This registry key is closed when the CloseEnum function is
// called.
//
if (!MprOpenKey(
HKEY_CURRENT_USER,
CONNECTION_KEY_NAME,
&(RememberInfo->ConnectKey),
DA_READ)) {
//
// We couldn't open the key. So we must assume that it doesn't
// exist because there if no connection information stored.
//
MPR_LOG(ERROR,"MprEnumRemembered: MprOpenKey Failed\n",0);
RememberInfo->ConnectKey = NULL;
return(WN_NO_MORE_ENTRIES);
}
}
//
// Find out the size of the largest key name.
//
if(!MprGetKeyInfo(
RememberInfo->ConnectKey,
NULL,
&numSubKeys,
&maxSubKeyLen,
NULL,
&maxValueLen)) {
MPR_LOG(ERROR,"MprEnumRemembered: MprGetKeyInfo Failed\n",0);
return(WN_CANNOT_OPEN_PROFILE);
}
//
// If we've already enumerated all the subkeys, there are no more entries.
//
if (RememberInfo->KeyIndex >= numSubKeys) {
return(WN_NO_MORE_ENTRIES);
}
tempBufPtr = lpBuffer;
bytesLeft = ROUND_DOWN(*lpBufferSize);
tempBufPtr = lpBuffer;
netResource.lpComment = NULL;
netResource.dwScope = RESOURCE_REMEMBERED;
netResource.dwUsage = 0;
netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
//
// MprReadConnectionInfo may access the providers
//
MprCheckProviders();
CProviderSharedLock PLock;
while(
(RememberInfo->KeyIndex < numSubKeys) &&
(entriesRead < *NumEntries) &&
(bytesLeft > sizeof(NETRESOURCE))
)
{
//
// Get the connection info from the key and stuff it into
// a NETRESOURCE structure.
//
BOOL fMatch = FALSE;
DWORD ProviderFlags; // ignored
DWORD DeferFlags; // ignored
if(!MprReadConnectionInfo(
RememberInfo->ConnectKey,
NULL,
RememberInfo->KeyIndex,
&ProviderFlags,
&DeferFlags,
&userName,
&netResource,
NULL,
maxSubKeyLen)) {
//
// NOTE: The ReadConnectionInfo call could return FALSE
// if it failed in a memory allocation.
//
if (entriesRead == 0) {
status = WN_NO_MORE_ENTRIES;
}
else {
status = WN_SUCCESS;
}
break;
}
else
{
if ((netResource.dwType == RememberInfo->ConnectionType) ||
(RememberInfo->ConnectionType == RESOURCETYPE_ANY)) {
fMatch = TRUE;
}
}
//
// Copy the new netResource information into the user's
// buffer. Each time this function is called, the tempBufPtr
// gets updated to point to the next free space in the user's
// buffer.
//
if ( fMatch )
{
status = MprCopyResource(
&tempBufPtr,
&netResource,
&bytesLeft);
if (status != WN_SUCCESS) {
if (entriesRead == 0) {
*lpBufferSize = ROUND_UP(bytesLeft);
status = WN_MORE_DATA;
}
else {
status = WN_SUCCESS;
}
break;
}
entriesRead++;
}
//
// Free the allocated memory resources.
//
LocalFree(netResource.lpLocalName);
LocalFree(netResource.lpRemoteName);
LocalFree(netResource.lpProvider);
if (userName != NULL) {
LocalFree(userName);
}
(RememberInfo->KeyIndex)++;
}
*NumEntries = entriesRead;
return(status);
}
DWORD
MprMultiStrBuffSize(
IN LPTSTR lpString1,
IN LPTSTR lpString2,
IN LPTSTR lpString3,
IN LPTSTR lpString4,
IN LPTSTR lpString5
)
/*++
Routine Description:
This function is a worker function that simply determines the total
storage requirements needed by the passed set of strings. Any of the
strings maybe NULL in which case the string will be ignored.
The NULL terminator is added into the total memory requirements.
Arguments:
lpString1 -> 5 - Pointers to valid strings or NULL.
Return Value:
The count of bytes required to store the passed strings.
Note:
--*/
{
DWORD cbRequired = 0 ;
if ( lpString1 != NULL )
{
cbRequired += (wcslen( lpString1 ) + 1) * sizeof(TCHAR);
}
if ( lpString2 != NULL )
{
cbRequired += (wcslen( lpString2 ) + 1) * sizeof(TCHAR) ;
}
if ( lpString3 != NULL )
{
cbRequired += (wcslen( lpString3 ) + 1) * sizeof(TCHAR) ;
}
if ( lpString4 != NULL )
{
cbRequired += (wcslen( lpString4 ) + 1) * sizeof(TCHAR) ;
}
if ( lpString5 != NULL )
{
cbRequired += (wcslen( lpString5 ) + 1) * sizeof(TCHAR) ;
}
return cbRequired ;
}
BOOL
MprNetIsAvailable(
VOID)
/*++
Routine Description:
This function checks if the net is available by calling the GetCaps
of each provider to make sure it is started.
Arguments:
none
Return Value:
TRUE is yes, FALSE otherwise
Note:
--*/
{
DWORD status = WN_SUCCESS;
LPDWORD index;
DWORD indexArray[DEFAULT_MAX_PROVIDERS];
DWORD numProviders, i;
LPPROVIDER provider;
DWORD dwResult ;
//
// Find the list of providers to call for this request.
// If there are no active providers, MprFindCallOrder returns
// WN_NO_NETWORK.
//
index = indexArray;
status = MprFindCallOrder(
NULL,
&index,
&numProviders,
NETWORK_TYPE);
if (status != WN_SUCCESS)
return(FALSE);
//
// Loop through the list of providers, making sure at least one
// is started
//
ASSERT(MPR_IS_INITIALIZED(NETWORK));
for (i=0; i<numProviders; i++)
{
//
// Call the appropriate providers API entry point
//
provider = GlobalProviderInfo + index[i];
if (provider->GetCaps != NULL)
{
dwResult = provider->GetCaps( WNNC_START );
if (dwResult != 0)
{
if (index != indexArray)
LocalFree(index);
return (TRUE) ;
}
}
}
//
// If memory was allocated by MprFindCallOrder, free it.
//
if (index != indexArray)
LocalFree(index);
return(FALSE);
}