windows-nt/Source/XPSP1/NT/admin/netui/shell/enum/wnetenum.cxx
2020-09-26 16:20:57 +08:00

841 lines
26 KiB
C++

/**********************************************************************/
/** Microsoft Windows/NT **/
/** Copyright(c) Microsoft Corp., 1991 **/
/**********************************************************************/
/*
WNETENUM.CXX
This file contains the implementation of
NPOpenEnum - open a resource enumeration handle
NPEnumResource - walk through all the resource
NPCloseEnum - end of walk through
FILE HISTORY:
terryk 27-Sep-91 Created
terryk 01-Nov-91 WIN32 conversion
terryk 08-Nov-91 Code review changes
terryk 18-Nov-91 Split to 2 files - wnetenum.cxx and
enumnode.cxx
terryk 10-Dec-91 check parameters in WNetOpenEnum
terryk 28-Dec-91 changed DWORD to UINT
Yi-HsinS31-Dec-91 Unicode work
terryk 03-Jan-92 Capitalize the Resource_XXX manifest
terryk 10-Jan-92 Returned WN_SUCCESS if the buffer is too
small for 1 entry.
*/
#define INCL_WINDOWS
#define INCL_DOSERRORS
#define INCL_NETERRORS
#define INCL_NETCONS
#define INCL_NETUSE
#define INCL_NETWKSTA
#define INCL_NETACCESS // NetPasswordSet declaration
#define INCL_NETCONFIG
#define INCL_NETREMUTIL
#define INCL_NETSHARE
#define INCL_NETSERVER
#define INCL_NETSERVICE
#define INCL_NETLIB
#define INCL_ICANON
#define _WINNETWK_
#include <lmui.hxx>
#undef _WINNETWK_
#define INCL_BLT_WINDOW
#include <blt.hxx>
#include <dbgstr.hxx>
#include <winnetwk.h>
#include <winnetp.h>
#include <npapi.h>
#include <wnetenum.h>
#include <winlocal.h>
#include <mnet.h>
#include <lmobj.hxx>
#include <lmoshare.hxx>
#include <lmoesh.hxx>
#include <lmoeuse.hxx>
#include <lmodev.hxx>
#include <lmosrv.hxx>
#include <lmowks.hxx>
#include <lmoesrv.hxx>
#include <lmsvc.hxx>
#include <uibuffer.hxx>
#include <uitrace.hxx>
#include <uiassert.hxx>
#include <uatom.hxx>
#include <regkey.hxx>
#include <array.hxx>
#include <string.hxx>
#include <strchlit.hxx> // for SERVER_INIT_STRING
#include <miscapis.hxx>
#include <wnetenum.hxx>
//
// Macros for rounding a value up/down to a TCHAR boundary.
// Note: These macros assume that sizeof(TCHAR) is a power of 2.
//
#define ROUND_DOWN(x) ((x) & ~(sizeof(TCHAR) - 1))
#define ROUND_UP(x) (((x) + sizeof(TCHAR) - 1) & ~(sizeof(TCHAR) - 1))
/*******************************************************************
Global variables
********************************************************************/
#define ARRAY_SIZE 64
extern NET_ENUM_HANDLE_TABLE *vpNetEnumArray;
/* Winnet locking handle
*/
HANDLE vhSemaphore ;
/* Name of the provider
*/
const TCHAR * pszNTLanMan = NULL ;
#define LM_WKSTA_NODE SZ("System\\CurrentControlSet\\Services\\LanmanWorkstation\\NetworkProvider")
#define LM_PROVIDER_VALUE_NAME SZ("Name")
/*******************************************************************
NAME: InitWNetEnum
SYNOPSIS: Initialize the Enum handle array
RETURN: APIERR - it will return ERROR_OUT_OF_MEMORY if it does
not have enough space
HISTORY:
terryk 24-Oct-91 Created
davidhov 20-Oct-92 updated REG_KEY usage
********************************************************************/
APIERR InitWNetEnum()
{
TRACEEOL( "NTLANMAN.DLL: InitWNetEnum()" );
vpNetEnumArray = new NET_ENUM_HANDLE_TABLE( ARRAY_SIZE );
if ( vpNetEnumArray == NULL )
{
DBGEOL( "NTLANMAN.DLL: InitWNetEnum() ERROR_NOT_ENOUGH_MEMORY" );
return ERROR_NOT_ENOUGH_MEMORY;
}
APIERR err = vpNetEnumArray->QueryError();
if ( !err )
{
if ( (vhSemaphore = ::CreateSemaphore( NULL, 1, 1, NULL )) == NULL)
{
err = ::GetLastError() ;
DBGEOL( "NTLANMAN.DLL: InitWNetEnum() semaphore error " << err );
}
}
return err ;
}
/********************************************************************
NAME: GetLMProviderName
SYNOPSIS: Get Provider Name into the global variable pszNTLanMan
RETURN: APIERR - it will return ERROR_OUT_OF_MEMORY if it does
not have enough space
HISTORY:
congpay 14-Dec-92 Created
********************************************************************/
APIERR GetLMProviderName()
{
if (pszNTLanMan)
{
return NERR_Success;
}
APIERR err = NERR_Success;
REG_KEY *pRegKeyFocusServer = NULL;
do { // error breakout
/* Traverse the registry and get the list of computer alert
* names.
*/
pRegKeyFocusServer = REG_KEY::QueryLocalMachine();
if ( ( pRegKeyFocusServer == NULL ) ||
((err = pRegKeyFocusServer->QueryError())) )
{
err = err? err : ERROR_NOT_ENOUGH_MEMORY ;
break ;
}
ALIAS_STR nlsRegKeyName( LM_WKSTA_NODE ) ;
REG_KEY regkeyLMProviderNode( *pRegKeyFocusServer, nlsRegKeyName );
REG_KEY_INFO_STRUCT regKeyInfo;
REG_VALUE_INFO_STRUCT regValueInfo ;
if ( (err = regkeyLMProviderNode.QueryError()) ||
(err = regkeyLMProviderNode.QueryInfo( &regKeyInfo )) )
{
break ;
}
BUFFER buf( (UINT) regKeyInfo.ulMaxValueLen ) ;
regValueInfo.nlsValueName = LM_PROVIDER_VALUE_NAME ;
if ( (err = buf.QueryError() ) ||
(err = regValueInfo.nlsValueName.QueryError()) )
{
break;
}
regValueInfo.pwcData = buf.QueryPtr();
regValueInfo.ulDataLength = buf.QuerySize() ;
if ( (err = regkeyLMProviderNode.QueryValue( &regValueInfo )))
{
break;
}
/* Null terminate the computer list string we just retrieved from
* the registry.
*/
TCHAR * pszProviderName = (TCHAR *)( buf.QueryPtr() +
regValueInfo.ulDataLengthOut -
sizeof(TCHAR) );
*pszProviderName = TCH('\0') ;
ALIAS_STR nlsComputerList( (TCHAR *) buf.QueryPtr()) ;
pszNTLanMan = new TCHAR[ nlsComputerList.QueryTextSize() ] ;
if ( pszNTLanMan == NULL )
{
err = ERROR_NOT_ENOUGH_MEMORY ;
break ;
}
nlsComputerList.CopyTo( (TCHAR *) pszNTLanMan,
nlsComputerList.QueryTextSize()) ;
} while (FALSE) ;
delete pRegKeyFocusServer ;
return err;
}
/*******************************************************************
NAME: TermWNetEnum
SYNOPSIS: clear up the Enum handle array
HISTORY:
terryk 24-Oct-91 Created
********************************************************************/
VOID TermWNetEnum()
{
TRACEEOL( "NTLANMAN.DLL: TermWNetEnum()" );
delete vpNetEnumArray;
vpNetEnumArray = NULL;
REQUIRE( ::CloseHandle( vhSemaphore ) ) ;
vhSemaphore = NULL ;
delete (void *) pszNTLanMan ;
pszNTLanMan = NULL ;
}
/*******************************************************************
NAME: NPOpenEnum
SYNOPSIS: Create a new Enum handle
ENTRY: UINT dwScope - determine the scope of the enumeration.
This can be one of:
RESOURCE_CONNECTED - all currently connected resource
RESOURCE_GLOBALNET - all resources on the network
RESOURCE_CONTEXT - resources in the user's current
and default network context
UINT dwType - used to specify the type of resources of
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.
UINT dwUsage - Used to specify the usage of resources
of interested. 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.
RESOURCEUSAGE_ATTACHED - signifies that the function
should fail if the caller is not authenticated (even
if the network allows enumeration without authenti-
cation).
This parameter is ignored if dwScope is not
RESOURCE_GLOBALNET.
NETRESOURCE * lpNetResource - This specifies the
container to perform the enumeration. The
NETRESOURCE must have been obtained via
NPEnumResource( and must have the
RESOURCEUSAGE_Connectable bit set ), or NULL. If it
is NULL,the logical root of the network is assumed.
An application would normally start off by calling
NPOpenEnum with this parameter set to NULL, and
then use the returned results for further
enumeration. If dwScope is RESOURCE_CONNECTED, this
must be NULL.
If dwScope is RESOURCE_CONTEXT, this is ignored.
HANDLE * lphEnum - If function call is successful, this
will contain a handle that can then be used for
NPEnumResource.
EXIT: HANDLE * lphEnum - will contain the handle number
RETURNS: WN_SUCCESS if the call is successful. Otherwise,
GetLastError should be called for extended error
information. Extened error codes include:
WN_NOT_CONTAINER - lpNetResource does not point to a
container
WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType,
or bad combination of parameters is specified
WN_NOT_NETWORK - network is not present
WN_NET_ERROR - a network specific error occured.
WNetGetLastError should be called to obtain further
information.
HISTORY:
terryk 24-Oct-91 Created
Johnl 06-Mar-1992 Added Computer validation check
on container enumeration
JohnL 03-Apr-1992 Fixed dwUsage == CONNECTED|CONTAINER
bug (would return WN_BAD_VALUE)
ChuckC 01-Aug-1992 Simplified, corrected and commented
the messy cases wrt to dwUsage.
AnirudhS 03-Mar-1995 Added support for RESOURCE_CONTEXT
AnirudhS 26-Apr-1996 Simplified, corrected and commented
the messy cases wrt dwScope.
********************************************************************/
DWORD APIENTRY
NPOpenEnum(
UINT dwScope,
UINT dwType,
UINT dwUsage,
LPNETRESOURCE lpNetResource,
HANDLE * lphEnum )
{
UIASSERT( lphEnum != NULL );
APIERR err ;
if ( err = CheckLMService() )
return err ;
if ( dwType & ~( RESOURCETYPE_DISK | RESOURCETYPE_PRINT ) )
{
return WN_BAD_VALUE;
}
NET_ENUMNODE *pNetEnum;
if ( dwScope == RESOURCE_CONNECTED )
{
/*
* we are looking for current uses
*/
if ( lpNetResource != NULL )
{
return WN_BAD_VALUE;
}
err = GetLMProviderName();
if (err)
return(err);
pNetEnum = new USE_ENUMNODE( dwScope, dwType, dwUsage, lpNetResource );
}
else if ( dwScope == RESOURCE_CONTEXT )
{
/*
* we are looking for servers in the domain
* Note that lpNetResource is ignored
* dwType is decoded in the CONTEXT_ENUMNODE constructor
*/
pNetEnum = new CONTEXT_ENUMNODE( dwScope, dwType, dwUsage, NULL );
}
else if ( dwScope == RESOURCE_SHAREABLE )
{
/*
* We are looking for shareable resources
* Use SHARE_ENUMNODE, which decodes dwScope when it enumerates
* If we're not given a server, return an EMPTY_ENUMNODE
*/
if (lpNetResource != NULL
&&
lpNetResource->lpRemoteName != NULL
&&
lpNetResource->lpRemoteName[0] == TCH('\\')
&&
lpNetResource->lpRemoteName[1] == TCH('\\'))
{
pNetEnum = new SHARE_ENUMNODE( dwScope, dwType, dwUsage, lpNetResource );
}
else
{
pNetEnum = new EMPTY_ENUMNODE( dwScope, dwType, dwUsage, lpNetResource );
}
}
else if ( dwScope == RESOURCE_GLOBALNET )
{
/* Look for the combination of all bits and substitute "All" for
* them. Ignore bits we don't know about.
* Note: RESOURCEUSAGE_ATTACHED is a no-op for us, since LanMan
* always tries to authenticate when doing an enumeration.
*/
dwUsage &= (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER);
if ( dwUsage == (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER) )
{
dwUsage = 0 ;
}
/*
* we are looking for global resources out on the net
*/
if ( lpNetResource == NULL || lpNetResource->lpRemoteName == NULL)
{
/*
* at top level, therefore enumerating domains. if user
* asked for connectable, well, there aint none.
*/
if ( dwUsage == RESOURCEUSAGE_CONNECTABLE )
{
pNetEnum = new EMPTY_ENUMNODE( dwScope,
dwType,
dwUsage,
lpNetResource );
}
else
{
pNetEnum = new DOMAIN_ENUMNODE( dwScope,
dwType,
dwUsage,
lpNetResource );
}
}
else
{
/*
* we are assured of lpRemoteName != NULL.
* things get interesting here. the cases are as follows:
*
* if (dwUsage == 0)
* if have \\ in front
* return shares
* else
* return servers
* else if (dwUsage == CONNECTABLE)
* if have \\ in front
* return shares
* else
* empty enum
* else if (dwUsage == CONTAINER)
* if have \\ in front
* empty enum
* else
* return server
*
* In interest of code size, i've reorganized the above
* cases to minimized the bodies of the ifs.
*
* chuckc.
*/
if ( ((dwUsage == RESOURCEUSAGE_CONNECTABLE) ||
(dwUsage == 0)
)
&&
((lpNetResource->lpRemoteName[0] == TCH('\\')) &&
(lpNetResource->lpRemoteName[1] == TCH('\\'))
)
)
{
/* Confirm that this really is a computer name (i.e., a
* container we can enumerate).
*/
if ( ::I_MNetNameValidate( NULL,
&(lpNetResource->lpRemoteName[2]),
NAMETYPE_COMPUTER,
0L))
{
return WN_BAD_VALUE ;
}
pNetEnum = new SHARE_ENUMNODE( dwScope, dwType, dwUsage,
lpNetResource );
}
else if ( ((dwUsage == RESOURCEUSAGE_CONTAINER) ||
(dwUsage == 0)
)
&&
(lpNetResource->lpRemoteName[0] != TCH('\\'))
)
{
pNetEnum = new SERVER_ENUMNODE( dwScope, dwType, dwUsage,
lpNetResource );
}
else if (
// ask for share but aint starting from server
(
(dwUsage == RESOURCEUSAGE_CONNECTABLE)
&&
(lpNetResource->lpRemoteName[0] != TCH('\\'))
)
||
// ask for server but is starting from server
(
(dwUsage == RESOURCEUSAGE_CONTAINER)
&&
((lpNetResource->lpRemoteName[0] == TCH('\\')) &&
(lpNetResource->lpRemoteName[1] == TCH('\\'))
)
)
)
{
pNetEnum = new EMPTY_ENUMNODE( dwScope,
dwType,
dwUsage,
lpNetResource );
}
else
{
// incorrect dwUsage
return WN_BAD_VALUE;
}
}
}
else
{
// invalid dwScope
return WN_BAD_VALUE;
}
if ( pNetEnum == NULL )
{
return WN_OUT_OF_MEMORY;
}
else if ( err = pNetEnum->QueryError() )
{
delete pNetEnum;
return MapError(err);
}
if ( pNetEnum->IsFirstGetInfo() )
{
if (( err = pNetEnum->GetInfo()) != WN_SUCCESS )
{
delete pNetEnum;
return MapError(err);
}
}
////////////////////////////////////////// Enter critical section
if ( err = WNetEnterCriticalSection() )
{
delete pNetEnum;
return err ;
}
ASSERT( vpNetEnumArray != NULL );
INT iPos = vpNetEnumArray->QueryNextAvail();
if ( iPos < 0 )
{
WNetLeaveCriticalSection() ;
delete pNetEnum;
return WN_OUT_OF_MEMORY;
}
vpNetEnumArray->SetNode( (UINT)iPos, pNetEnum );
*lphEnum = UintToPtr((UINT)iPos);
WNetLeaveCriticalSection() ;
////////////////////////////////////////// Leave critical section
return err ;
}
/*******************************************************************
NAME: NPEnumResource
SYNOPSIS: Perform an enumeration based on handle returned by
NPOpenEnum.
ENTRY: HANDLE hEnum - This must be a handle obtained from
NPOpenEnum call
UINT *lpcRequested - Specifies the number of entries
requested. It may be 0xFFFFFFFF to request as many as
possible. On successful call, this location will receive
the number of entries actually read.
VOID *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.
UINT * lpBufferSize - This specifies the size of the
buffer passed to the function call. If WN_MORE_DATA
is returned and no entries were enumerated, then this
will be set to the minimum buffer size required.
EXIT: UINT *lpcRequested - will receive the number of entries
actually read.
RETURNS: WN_SUCCESS if the call is successful, the caller should
continue to call NPEnumResource to continue the
enumeration.
WN_NO_MORE_ENTRIES - no more entries found, the
enumeration completed successfully ( the contents of the
return buffer is undefined). Otherwise, GetLastError
should be called for extended error information.
Extended error codes include:
WN_MORE_DATA - the buffer is too small even for one
entry
WN_BAD_HANDLE - hEnum is not a valid handle
WN_NOT_NETWORK - network is not present. This
condition is checked for before hEnum is tested for
validity.
WN_NET_ERROR - a network specific error occured.
WNetGetLastError should be called to obtain further
information.
HISTORY:
terryk 24-Oct-91 Created
KeithMo 15-Sep-92 Align *lpcBufferSize as needed.
********************************************************************/
DWORD APIENTRY
NPEnumResource(
HANDLE hEnum,
UINT * lpcRequested,
LPVOID lpBuffer,
UINT * lpcBufferSize )
{
APIERR err ;
if (( lpBuffer == NULL ) ||
( lpcRequested == NULL ) ||
( lpcBufferSize == NULL ))
{
return WN_BAD_VALUE;
}
if ( err = WNetEnterCriticalSection() )
{
return err ;
}
ASSERT( vpNetEnumArray != NULL );
NET_ENUMNODE *pNode = vpNetEnumArray->QueryNode(PtrToUint(hEnum));
WNetLeaveCriticalSection() ;
if ( pNode == NULL )
{
return WN_BAD_HANDLE;
}
else if ( pNode->IsFirstGetInfo() )
{
if ( err = CheckLMService() )
{
return err ;
}
if (( err = pNode->GetInfo()) != WN_SUCCESS )
{
return ( MapError(err) );
}
}
LPNETRESOURCE pNetResource = ( LPNETRESOURCE ) lpBuffer;
UINT cbRemainSize = ROUND_DOWN(*lpcBufferSize);
UINT cRequested = (*lpcRequested);
*lpcRequested = 0;
while ( *lpcRequested < cRequested )
{
err = pNode->GetNetResource((BYTE *)pNetResource, &cbRemainSize );
if ( err == WN_MORE_DATA )
{
/* If we can't even fit one into the buffer, then set the required
* buffer size and return WN_MORE_DATA.
*/
if ( *lpcRequested == 0 )
{
*lpcBufferSize = ROUND_UP(cbRemainSize);
}
else
{
err = NERR_Success ;
}
break;
}
if ( err == WN_NO_MORE_ENTRIES )
{
if ( *lpcRequested != 0 )
{
err = NERR_Success ;
}
break;
}
if ( err != WN_SUCCESS )
{
break ;
}
/* err == WN_SUCCESS
*/
(*lpcRequested) ++;
if ( sizeof( NETRESOURCE ) > cbRemainSize )
{
break ;
}
pNetResource ++;
cbRemainSize -= (UINT)sizeof( NETRESOURCE );
}
return err ;
}
/*******************************************************************
NAME: NPCloseEnum
SYNOPSIS: Closes an enumeration.
ENTRY: HANDLE hEnum - this must be a handle obtained from
NPOpenEnum call.
RETURNS: WN_SUCCESS if the call is successful. Otherwise,
GetLastError should be called for extended error information.
Extended error codes include:
WN_NO_NETWORK - network is not present. this condition is
checked for before hEnum is tested for validity.
WN_BAD_HANDLE - hEnum is not a valid handle.
WN_NET_ERROR - a network specific error occured.
WNetGetLastError should be called to obtain further
information.
HISTORY:
terryk 24-Oct-91 Created
********************************************************************/
DWORD APIENTRY
NPCloseEnum(
HANDLE hEnum )
{
APIERR err ;
if ( err = WNetEnterCriticalSection() )
{
return err ;
}
ASSERT( vpNetEnumArray != NULL );
NET_ENUMNODE *pNode = vpNetEnumArray->QueryNode(PtrToUint(hEnum));
if ( pNode == NULL )
{
// cannot find the node
err = WN_BAD_HANDLE;
}
else
{
vpNetEnumArray->ClearNode(PtrToUint(hEnum));
}
WNetLeaveCriticalSection() ;
return err ;
}
/*******************************************************************
NAME: WNetEnterCriticalSection
SYNOPSIS: Locks the LM network provider enumeration code
EXIT: vhSemaphore will be locked
RETURNS: NERR_Success if successful, error code otherwise
NOTES: We wait for 7 seconds for the semaphonre to be freed
HISTORY:
Johnl 27-Apr-1992 Created
********************************************************************/
APIERR WNetEnterCriticalSection( void )
{
APIERR err = NERR_Success ;
switch( WaitForSingleObject( vhSemaphore, 7000L ))
{
case 0:
break ;
case WAIT_TIMEOUT:
err = WN_FUNCTION_BUSY ;
break ;
case 0xFFFFFFFF:
err = ::GetLastError() ;
break ;
default:
UIASSERT(FALSE) ;
err = WN_WINDOWS_ERROR ;
break ;
}
return err ;
}
/*******************************************************************
NAME: WNetLeaveCriticalSection
SYNOPSIS: Unlocks the enumeration methods
RETURNS:
NOTES:
HISTORY:
Johnl 27-Apr-1992 Created
********************************************************************/
void WNetLeaveCriticalSection( void )
{
REQUIRE( ReleaseSemaphore( vhSemaphore, 1, NULL ) ) ;
}