diff options
Diffstat (limited to 'private/nw/svcdlls/nwwks/server/enum.c')
-rw-r--r-- | private/nw/svcdlls/nwwks/server/enum.c | 7595 |
1 files changed, 7595 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/server/enum.c b/private/nw/svcdlls/nwwks/server/enum.c new file mode 100644 index 000000000..38b3d859f --- /dev/null +++ b/private/nw/svcdlls/nwwks/server/enum.c @@ -0,0 +1,7595 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + enum.c + +Abstract: + + This module contains server, volume, and directory enumeration + routines supported by NetWare Workstation service. + +Author: + + Rita Wong (ritaw) 15-Feb-1993 + +Revision History: + +--*/ + +#include <stdlib.h> +#include <nw.h> +#include <splutil.h> +#include <nwmisc.h> +#include <nwreg.h> +#include <nds.h> +#include <nwapi32.h> + +//-------------------------------------------------------------------// +// // +// Definitions // +// // +//-------------------------------------------------------------------// + +// +// Other definitions +// +#define ONE_KB 1024 +#define TWO_KB 2048 +#define FOUR_KB 4096 +#define EIGHT_KB 8192 + +#define TREECHAR L'*' + +#define NW_VOLUME_NAME_LEN 256 +#define NW_MAX_VOLUME_NUMBER 64 + +// +// This structure is orginally defined in nwapi32.c, it is redefined +// here so that the routine NWGetFileServerVersionInfo() can be called +// with it. +// +typedef struct _NWC_SERVER_INFO { + HANDLE hConn ; + UNICODE_STRING ServerString ; +} NWC_SERVER_INFO ; + +//-------------------------------------------------------------------// +// // +// Local Function Prototypes // +// // +//-------------------------------------------------------------------// + +DWORD +NwrOpenEnumServersCommon( + IN NW_ENUM_TYPE EnumType, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ); + +DWORD +NwrOpenEnumCommon( + IN LPWSTR ContainerName, + IN NW_ENUM_TYPE EnumType, + IN DWORD StartingPoint, + IN BOOL ValidateUserFlag, + IN LPWSTR UserName OPTIONAL, + IN LPWSTR Password OPTIONAL, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + OUT LPDWORD ClassTypeOfNDSLeaf, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ); + +DWORD +NwEnumContextInfo( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumServersAndNdsTrees( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumPrintServers( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumVolumes( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumNdsSubTrees_Disk( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumNdsSubTrees_Print( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumNdsSubTrees_Any( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumVolumesQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumDirectories( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwEnumPrintQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ); + +DWORD +NwGetFirstDirectoryEntry( + IN HANDLE DirHandle, + OUT LPWSTR *DirEntry + ); + +DWORD +NwGetNextDirectoryEntry( + IN HANDLE DirHandle, + OUT LPWSTR *DirEntry + ); + +DWORD +NwGetFirstNdsSubTreeEntry( + OUT LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD BufferSize + ); + +DWORD +NwGetNextNdsSubTreeEntry( + OUT LPNW_ENUM_CONTEXT ContextHandle + ); + +BYTE +NwGetSubTreeData( + IN DWORD NdsRawDataPtr, + OUT LPDWORD SubTreeName, + OUT LPDWORD ResourceScope, + OUT LPDWORD ResourceType, + OUT LPDWORD ResourceDisplayType, + OUT LPDWORD ResourceUsage, + OUT LPWSTR * StrippedObjectName + ); + +VOID +NwStripNdsUncName( + IN LPWSTR ObjectName, + OUT LPWSTR * StrippedObjectName + ); + +#define VERIFY_ERROR_NOT_A_NDS_TREE 0x1010FFF0 +#define VERIFY_ERROR_PATH_NOT_FOUND 0x1010FFF1 + +DWORD +NwVerifyNDSObject( + IN LPWSTR lpNDSObjectNamePath, + OUT LPWSTR * lpFullNDSObjectNamePath, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ); + +DWORD +NwVerifyBinderyObject( + IN LPWSTR lpBinderyObjectNamePath, + OUT LPWSTR * lpFullBinderyObjectNamePath, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ); + +DWORD +NwGetNDSPathInfo( + IN LPWSTR lpNDSObjectNamePath, + OUT LPWSTR * lpSystemObjectNamePath, + OUT LPWSTR * lpSystemPathPart, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ); + +DWORD +NwGetBinderyPathInfo( + IN LPWSTR lpBinderyObjectNamePath, + OUT LPWSTR * lpSystemObjectNamePath, + OUT LPWSTR * lpSystemPathPart, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ); + +BOOL +NwGetRemoteNameParent( + IN LPWSTR lpRemoteName, + OUT LPWSTR * lpRemoteNameParent + ); + +int _CRTAPI1 +SortFunc( + IN CONST VOID *p1, + IN CONST VOID *p2 + ); + +DWORD +NwGetConnectionInformation( + IN LPWSTR lpName, + OUT LPWSTR lpUserName, + OUT LPWSTR lpHostServer + ); + + +VOID +NwpGetUncInfo( + IN LPWSTR lpstrUnc, + OUT WORD * slashCount, + OUT BOOL * isNdsUnc, + OUT LPWSTR * FourthSlash + ); + +DWORD +NwpGetCurrentUserRegKey( + IN DWORD DesiredAccess, + OUT HKEY *phKeyCurrentUser + ); + +DWORD +NwQueryInfo( + OUT LPWSTR *ppszPreferredSrv + ); + + +DWORD +NwrOpenEnumContextInfo( + IN LPWSTR Reserved OPTIONAL, + IN DWORD ConnectionType, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function creates a new context handle and initializes it + for enumerating context information (i.e. NDS user context objects + and/or NetWare bindery server connections). + +Arguments: + + Reserved - Unused. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could + not be allocated. + + NO_ERROR - Call was successful. + +--*/ +{ + LPWSTR pszCurrentContext = NULL; + DWORD dwPrintOptions; + DWORD status = NwQueryInfo( &pszCurrentContext ); + WCHAR Context[MAX_NDS_NAME_CHARS]; + LPNW_ENUM_CONTEXT ContextHandle; + + UNREFERENCED_PARAMETER(Reserved); + UNREFERENCED_PARAMETER(ConnectionType); + + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumContextInfo\n")); + } +#endif + + if ( status == NO_ERROR ) + { + if ( pszCurrentContext[0] == TREECHAR ) + { + wcscpy( Context, L"\\\\" ); + wcscat( Context, pszCurrentContext + 1 ); + + LocalFree( pszCurrentContext ); + pszCurrentContext = NULL; + + return NwrOpenEnumCommon( + Context, + NwsHandleListContextInfo_Tree, + 0xFFFFFFFF, + FALSE, + NULL, + NULL, + 0, + 0, + NULL, + EnumHandle + ); + } + else + { + // + // The user does not have a preferred NDS tree and context. They + // may have only a preferred server. + // + if ( pszCurrentContext[0] != 0 ) + { + // + // There is a prefered server. + // + LocalFree( pszCurrentContext ); + pszCurrentContext = NULL; + + ContextHandle = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + sizeof(NW_ENUM_CONTEXT) + ); + + if (ContextHandle == NULL) + { + KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Initialize contents of the context handle structure. + // + ContextHandle->Signature = NW_HANDLE_SIGNATURE; + ContextHandle->HandleType = NwsHandleListContextInfo_Server; + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + ContextHandle->ResumeId = 0xFFFFFFFF; + + // The following are set to zero due to the LMEM_ZEROINIT. + // ContextHandle->NdsRawDataBuffer = 0; + // ContextHandle->NdsRawDataSize = 0; + // ContextHandle->NdsRawDataId = 0; + // ContextHandle->NdsRawDataCount = 0; + // ContextHandle->TreeConnectionHandle = 0; + // ContextHandle->ConnectionType = 0; + + // + // Return the newly created context. + // + *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle; + + return NO_ERROR; + } + } + } + + // + // There is no information in the registry about the current user. + // We go ahead and make an enumeration handle and return success. + // Later, during a call to NPEnumResource, we will return zero items. + // This is done because there is no valid return code to tell the + // callee that we have no context information to provide. + // + ContextHandle = (PVOID) LocalAlloc( LMEM_ZEROINIT, + sizeof(NW_ENUM_CONTEXT) ); + + if (ContextHandle == NULL) + { + KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Initialize contents of the context handle structure. + // + ContextHandle->Signature = NW_HANDLE_SIGNATURE; + ContextHandle->HandleType = NwsHandleListContextInfo_Server; + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + ContextHandle->ResumeId = 0; // This will tell NwrEnum to + // give up (i.e. we are done). + + // The following are set to zero due to the LMEM_ZEROINIT. + // ContextHandle->NdsRawDataBuffer = 0; + // ContextHandle->NdsRawDataSize = 0; + // ContextHandle->NdsRawDataId = 0; + // ContextHandle->NdsRawDataCount = 0; + // ContextHandle->TreeConnectionHandle = 0; + // ContextHandle->ConnectionType = 0; + + // + // Return the newly created context. + // + *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle; + + return NO_ERROR; +} + + + +DWORD +NwrOpenEnumServersAndNdsTrees( + IN LPWSTR Reserved OPTIONAL, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function creates a new context handle and initializes it + for enumerating the servers and NDS trees on the network. + +Arguments: + + Reserved - Unused. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could + not be allocated. + + NO_ERROR - Call was successful. + +--*/ // NwrOpenEnumServersAndNdsTrees +{ + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint( ("\nNWWORKSTATION: NwrOpenEnumServersAndNdsTrees\n") ); + } +#endif + + return NwrOpenEnumServersCommon( + NwsHandleListServersAndNdsTrees, + EnumHandle + ); +} + + + +DWORD +NwOpenEnumPrintServers( + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function creates a new context handle and initializes it + for enumerating the print servers on the network. + +Arguments: + + Reserved - Unused. + EnumHandle - Receives the newly created context handle. + +Return Value: + + ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could + not be allocated. + + NO_ERROR - Call was successful. + +--*/ // NwOpenEnumPrintServers +{ + +#if DBG + IF_DEBUG(ENUM) { + KdPrint( ("\nNWWORKSTATION: NwOpenEnumPrintServers\n") ); + } +#endif + + return NwrOpenEnumServersCommon( + NwsHandleListPrintServers, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumVolumes( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ServerName, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the volumes on a server. + +Arguments: + + Reserved - Unused. + + ServerName - Supplies the name of the server to enumerate volumes. + This name is prefixed by \\. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumVolumes +{ + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumes %ws\n", + ServerName)); + } +#endif + + return NwrOpenEnumCommon( + ServerName, + NwsHandleListVolumes, + 0, + FALSE, + NULL, + NULL, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumNdsSubTrees_Disk( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ParentPathName, + OUT LPDWORD ClassTypeOfNDSLeaf, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the DISK object types + and containers of a sub-tree in a NDS tree. + +Arguments: + + Reserved - Unused. + + ParentPathName - Supplies the name of the tree and the path to a container + to enumerate sub-trees. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumNdsSubTrees_Disk +{ + + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Disk %ws\n", + ParentPathName)); + } +#endif + + return NwrOpenEnumCommon( + ParentPathName, + NwsHandleListNdsSubTrees_Disk, + 0, + FALSE, + NULL, + NULL, + 0, + 0, + ClassTypeOfNDSLeaf, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumNdsSubTrees_Print( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ParentPathName, + OUT LPDWORD ClassTypeOfNDSLeaf, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the PRINT object types + and containers of a sub-tree in a NDS tree. + +Arguments: + + Reserved - Unused. + + ParentPathName - Supplies the name of the tree and the path to a container + to enumerate sub-trees. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumNdsSubTrees_Print +{ +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Print %ws\n", + ParentPathName)); + } +#endif + + return NwrOpenEnumCommon( + ParentPathName, + NwsHandleListNdsSubTrees_Print, + 0, + FALSE, + NULL, + NULL, + 0, + 0, + ClassTypeOfNDSLeaf, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumNdsSubTrees_Any( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ParentPathName, + OUT LPDWORD ClassTypeOfNDSLeaf, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the ANY object types + and containers of a sub-tree in a NDS tree. + +Arguments: + + Reserved - Unused. + + ParentPathName - Supplies the name of the tree and the path to a container + to enumerate sub-trees. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumNdsSubTrees_Any +{ + + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Any %ws\n", + ParentPathName)); + } +#endif + + return NwrOpenEnumCommon( + ParentPathName, + NwsHandleListNdsSubTrees_Any, + 0, + FALSE, + NULL, + NULL, + 0, + 0, + ClassTypeOfNDSLeaf, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumQueues( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ServerName, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the volumes on a server. + +Arguments: + + Reserved - Unused. + + ServerName - Supplies the name of the server to enumerate volumes. + This name is prefixed by \\. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumQueues +{ + + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumQueues %ws\n", + ServerName)); + } +#endif + + return NwrOpenEnumCommon( + ServerName, + NwsHandleListQueues, + 0xFFFFFFFF, + TRUE, + NULL, + NULL, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumVolumesQueues( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ServerName, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the volumes/queues on a server. + +Arguments: + + Reserved - Unused. + + ServerName - Supplies the name of the server to enumerate volumes. + This name is prefixed by \\. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwrOpenEnumVolumesQueues +{ + + DWORD status; + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumesQueues %ws\n", + ServerName)); + } +#endif + + status = NwrOpenEnumCommon( + ServerName, + NwsHandleListVolumesQueues, + 0, + FALSE, + NULL, + NULL, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + EnumHandle + ); + + if ( status == NO_ERROR ) + ((LPNW_ENUM_CONTEXT) *EnumHandle)->ConnectionType = CONNTYPE_DISK; + + return status; +} + + +DWORD +NwrOpenEnumDirectories( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR ParentPathName, + IN LPWSTR UserName OPTIONAL, + IN LPWSTR Password OPTIONAL, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the volumes on a server. + +Arguments: + + Reserved - Unused. + + ParentPathName - Supplies the parent path name in the format of + \\Server\Volume. + + UserName - Supplies the username to connect with. + + Password - Supplies the password to connect with. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ //NwrOpenEnumDirectories +{ + UNREFERENCED_PARAMETER(Reserved); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwrOpenEnumDirectories %ws\n", + ParentPathName)); + } +#endif + + return NwrOpenEnumCommon( + ParentPathName, + NwsHandleListDirectories, + 0, + FALSE, + UserName, + Password, + FILE_CREATE, + FILE_CREATE_TREE_CONNECTION | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + EnumHandle + ); +} + + +DWORD +NwOpenEnumPrintQueues( + IN LPWSTR ServerName, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function calls a common routine which creates a new context + handle and initializes it for enumerating the print queues on a server. + +Arguments: + + Reserved - Unused. + + ServerName - Supplies the name of the server to enumerate volumes. + This name is prefixed by \\. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + NO_ERROR or reason for failure. + +--*/ // NwOpenEnumPrintQueues +{ + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("\nNWWORKSTATION: NwOpenEnumPrintQueues %ws\n", + ServerName)); + } +#endif + + return NwrOpenEnumCommon( + ServerName, + NwsHandleListPrintQueues, + 0xFFFFFFFF, + TRUE, + NULL, + NULL, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + EnumHandle + ); +} + + +DWORD +NwrOpenEnumServersCommon( + IN NW_ENUM_TYPE EnumType, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function creates a new context handle and initializes it + for enumerating the servers on the network. + +Arguments: + + EnumType - Supplies the type of the object we want to enumerate + + EnumHandle - Receives the newly created context handle. + +Return Value: + + ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could + not be allocated. + + NO_ERROR - Call was successful. + +--*/ // NwrOpenEnumServersCommon +{ + DWORD status = NO_ERROR; + LPNW_ENUM_CONTEXT ContextHandle = NULL; + + // + // Allocate memory for the context handle structure. + // + ContextHandle = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + sizeof(NW_ENUM_CONTEXT) + ); + + if (ContextHandle == NULL) { + KdPrint(( + "NWWORKSTATION: NwrOpenEnumServersCommon LocalAlloc Failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Initialize contents of the context handle structure. + // + ContextHandle->Signature = NW_HANDLE_SIGNATURE; + ContextHandle->HandleType = EnumType; + ContextHandle->ResumeId = 0xFFFFFFFF; + ContextHandle->NdsRawDataBuffer = 0x00000000; + ContextHandle->NdsRawDataSize = 0x00000000; + ContextHandle->NdsRawDataId = 0x00000000; + ContextHandle->NdsRawDataCount = 0x00000000; + + // + // Set flag to indicate that we are going to enumerate NDS trees first. + // + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS; + + // + // Impersonate the client + // + if ((status = NwImpersonateClient()) != NO_ERROR) + { + goto CleanExit; + } + + // + // We enum servers and nds trees from the preferred server. + // + status = NwOpenPreferredServer( + &ContextHandle->TreeConnectionHandle + ); + + (void) NwRevertToSelf() ; + + if (status == NO_ERROR) + { + // + // Return the newly created context. + // + *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle; + + return status; + } + +CleanExit: + if ( ContextHandle ) + { + ContextHandle->Signature = 0x0BADBAD0; + + (void) LocalFree((HLOCAL) ContextHandle); + } + + return status; +} + + +DWORD +NwrOpenEnumCommon( + IN LPWSTR ContainerName, + IN NW_ENUM_TYPE EnumType, + IN DWORD StartingPoint, + IN BOOL ValidateUserFlag, + IN LPWSTR UserName OPTIONAL, + IN LPWSTR Password OPTIONAL, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + OUT LPDWORD ClassTypeOfNDSLeaf, + OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function is common code for creating a new context handle + and initializing it for enumerating either volumes, directories, + or NDS subtrees. + +Arguments: + + ContainerName - Supplies the full path name to the container object + we are enumerating from. + + EnumType - Supplies the type of the object we want to enumerate + + StartingPoint - Supplies the initial resume ID. + + UserName - Supplies the username to connect with. + + Password - Supplies the password to connect with. + + EnumHandle - Receives the newly created context handle. + +Return Value: + + ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could + not be allocated. + + NO_ERROR - Call was successful. + + Other errors from failure to open a handle to the server. + +--*/ // NwrOpenEnumCommon +{ + DWORD status = NO_ERROR; + NTSTATUS ntstatus = STATUS_SUCCESS; + LPNW_ENUM_CONTEXT ContextHandle = NULL; + LPWSTR StrippedContainerName = NULL; + BOOL fImpersonate = FALSE ; + + if ( ClassTypeOfNDSLeaf ) + *ClassTypeOfNDSLeaf = 0; + + // + // Before we do anything, we need to convert the UNC passed to + // us. We need to get rid of any CN=XXX.OU=YYY.O=ZZZ references, and + // convert them to XXX.YYY.ZZZ format. Any NETRESOURCE that we generate + // will look like \\TREE\XXX.YYY.ZZZ for a NDS Unc. We do this to + // work around to a bug in WOW.EXE, that prevents 16 bit apps from + // being launched when the user types NDS paths with the CN= stuff in it. + // + NwStripNdsUncName( ContainerName, &StrippedContainerName ); + + if ( StrippedContainerName == NULL ) + { + KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Allocate memory for the context handle structure and space for + // the ContainerName plus \. Now need one more for NULL terminator + // because it's already included in the structure. + // + ContextHandle = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + sizeof(NW_ENUM_CONTEXT) + + (wcslen(StrippedContainerName) + 1) * sizeof(WCHAR) + ); + + if (ContextHandle == NULL) + { + if ( StrippedContainerName ) + { + (void) LocalFree((HLOCAL) StrippedContainerName); + StrippedContainerName = NULL; + } + + KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Initialize contents of the context handle structure. + // + ContextHandle->Signature = NW_HANDLE_SIGNATURE; + ContextHandle->HandleType = EnumType; + ContextHandle->ResumeId = StartingPoint; + + // + // These are set to zero due to LMEM_ZEROINIT. + // + // ContextHandle->NdsRawDataBuffer = 0; + // ContextHandle->NdsRawDataSize = 0; + // ContextHandle->NdsRawDataId = 0; + // ContextHandle->NdsRawDataCount = 0; + // ContextHandle->TreeConnectionHandle = 0; + + // + // Impersonate the client + // + if ( ( status = NwImpersonateClient() ) != NO_ERROR ) + { + goto ErrorExit; + } + + fImpersonate = TRUE; + + if ( EnumType == NwsHandleListNdsSubTrees_Disk || + EnumType == NwsHandleListNdsSubTrees_Print || + EnumType == NwsHandleListNdsSubTrees_Any || + EnumType == NwsHandleListContextInfo_Tree ) + { + WCHAR lpServerName[NW_MAX_SERVER_LEN]; + UNICODE_STRING ServerName; + UNICODE_STRING ObjectName; + + ServerName.Length = 0; + ServerName.MaximumLength = sizeof( lpServerName ); + ServerName.Buffer = lpServerName; + + ObjectName.Buffer = NULL; + + if ( EnumType == NwsHandleListContextInfo_Tree ) + { + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + } + else + { + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS; + } + + ObjectName.MaximumLength = ( wcslen( StrippedContainerName ) + 1 ) * + sizeof( WCHAR ); + + ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer, + StrippedContainerName, + PARSE_NDS_GET_TREE_NAME ); + + if ( ObjectName.Length == 0 || ObjectName.Buffer == NULL ) + { + status = ERROR_PATH_NOT_FOUND; + goto ErrorExit; + } + + // + // Open a NDS tree connection handle to \\treename + // + ntstatus = NwNdsOpenTreeHandle( &ObjectName, + &ContextHandle->TreeConnectionHandle ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + + // + // Get the path to the container to open. + // + ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer, + StrippedContainerName, + PARSE_NDS_GET_PATH_NAME + ); + + if ( ObjectName.Length == 0 ) + { + UNICODE_STRING Root; + + RtlInitUnicodeString(&Root, L"[Root]"); + + // + // Resolve the path to get a NDS object id of [Root]. + // + ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle, + &Root, + &ContextHandle->dwOid, + &ServerName, + NULL, + 0 ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + wcscpy(ContextHandle->ContainerName, StrippedContainerName); + } + else + { + // + // Resolve the path to get a NDS object id. + // + ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle, + &ObjectName, + &ContextHandle->dwOid, + &ServerName, + NULL, + 0 ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + wcscpy(ContextHandle->ContainerName, StrippedContainerName); + } + + if ( ServerName.Length ) + { + DWORD dwHandleType; + + // + // NwNdsResolveName succeeded, but we were referred to + // another server, though ContextHandle->dwOid is still valid. + + if ( ContextHandle->TreeConnectionHandle ) + CloseHandle( ContextHandle->TreeConnectionHandle ); + + ContextHandle->TreeConnectionHandle = 0; + + // + // Open a NDS generic connection handle to \\ServerName + // + ntstatus = NwNdsOpenGenericHandle( &ServerName, + &dwHandleType, + &ContextHandle->TreeConnectionHandle ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + ASSERT( dwHandleType == HANDLE_TYPE_NCP_SERVER ); + } + + // + // Check to see if object is either a Server, Directory Map, or Volume. + // If so, the object is a known leaf in terms of NDS, and therefore cannot + // be enumerated through NwNdsList API calls. We fail the OpenEnum call in these + // cases and pass back the type of object the leaf node was. This way the code in + // NWPROVAU!NPOpenEnum can call NwrOpenEnumServer, NwrOpenEnumVolume, or + // NwrOpenEnumDirectories accordingly. + // + { + BYTE RawResponse[TWO_KB]; + DWORD RawResponseSize = sizeof(RawResponse); + DWORD dwStrLen; + PBYTE pbRawGetInfo; + + ntstatus = NwNdsReadObjectInfo( ContextHandle->TreeConnectionHandle, + ContextHandle->dwOid, + RawResponse, + RawResponseSize ); + + if ( ntstatus != NO_ERROR ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + (void) NwRevertToSelf() ; + fImpersonate = FALSE; + + pbRawGetInfo = RawResponse; + + // + // The structure of a NDS_RESPONSE_GET_OBJECT_INFO consists of 4 DWORDs + // followed by two standard NDS format UNICODE strings. Below we jump pbRawGetInfo + // into the buffer, past the 4 DWORDs. + // + pbRawGetInfo += sizeof ( NDS_RESPONSE_GET_OBJECT_INFO ); + + // + // Now we get the length of the first string (Base Class). + // + dwStrLen = * ( DWORD * ) pbRawGetInfo; + + // + // Now we point pbRawGetInfo to the first WCHAR of the first string (Base Class). + // + pbRawGetInfo += sizeof( DWORD ); + + // + // If the object is either a NCP Server, Volume, or a Directory Map, we fail + // the OpenEnum call and return the class type of the NDS leaf object. We do + // this because we cannot enumerate through NwNdsList() calls any subordinates, + // all browsing below these types are done through system redirector calls. So + // the client side of the provider will instead call NwOpenEnumVolumes or + // NwOpenEnumDirectories, respectively. + // + if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"NCP Server" ) ) + { + if ( ClassTypeOfNDSLeaf ) + *ClassTypeOfNDSLeaf = CLASS_TYPE_NCP_SERVER; + status = ERROR_NETWORK_ACCESS_DENIED; + goto ErrorExit; + } + + if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Volume" ) ) + { + if ( ClassTypeOfNDSLeaf ) + *ClassTypeOfNDSLeaf = CLASS_TYPE_VOLUME; + status = ERROR_NETWORK_ACCESS_DENIED; + goto ErrorExit; + } + + if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Directory Map" ) ) + { + if ( ClassTypeOfNDSLeaf ) + *ClassTypeOfNDSLeaf = CLASS_TYPE_DIRECTORY_MAP; + status = ERROR_NETWORK_ACCESS_DENIED; + goto ErrorExit; + } + } // End of block + } + else // EnumType is something other than a NDS Sub-tree + { + UNICODE_STRING TreeConnectStr; + + TreeConnectStr.Buffer = NULL; + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + + wcscpy(ContextHandle->ContainerName, StrippedContainerName); + wcscat(ContextHandle->ContainerName, L"\\"); + + // + // Open a tree connection handle to \Device\NwRdr\ContainerName + // + status = NwCreateTreeConnectName( StrippedContainerName, + NULL, + &TreeConnectStr ); + + if ( status != NO_ERROR ) + { + goto ErrorExit; + } + + status = NwOpenCreateConnection( &TreeConnectStr, + UserName, + Password, + StrippedContainerName, + FILE_LIST_DIRECTORY | SYNCHRONIZE | + ( ValidateUserFlag? FILE_WRITE_DATA : 0 ), + CreateDisposition, + CreateOptions, + RESOURCETYPE_DISK, // When connecting beyond servername + &ContextHandle->TreeConnectionHandle, + NULL ); + + (void) LocalFree((HLOCAL) TreeConnectStr.Buffer); + } + + if (status == NO_ERROR) + { + VERSION_INFO vInfo; + + if ( EnumType == NwsHandleListVolumes || + EnumType == NwsHandleListVolumesQueues ) + { + NWC_SERVER_INFO ServerInfo; + + ServerInfo.hConn = ContextHandle->TreeConnectionHandle; + ServerInfo.ServerString.Length = 0; + ServerInfo.ServerString.MaximumLength = 0; + ServerInfo.ServerString.Buffer = NULL; + + status = NWGetFileServerVersionInfo( (HANDLE) &ServerInfo, + &vInfo ); + + if ( status ) + { + ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER; + status = NO_ERROR; + } + else + { + ContextHandle->dwMaxVolumes = (DWORD) vInfo.maxVolumes; + + if ( ContextHandle->dwMaxVolumes == 0 ) + { + ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER; + } + } + } + + (void) NwRevertToSelf() ; + fImpersonate = FALSE; + + if ( StrippedContainerName ) + { + (void) LocalFree((HLOCAL) StrippedContainerName); + StrippedContainerName = NULL; + } + + // + // Return the newly created context. + // + *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle; + + return status; + } + +ErrorExit: + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( StrippedContainerName ) + { + (void) LocalFree((HLOCAL) StrippedContainerName); + } + + if ( ContextHandle ) + { + if ( ContextHandle->TreeConnectionHandle ) + CloseHandle( ContextHandle->TreeConnectionHandle ); + + ContextHandle->Signature = 0x0BADBAD0; + + (void) LocalFree((HLOCAL) ContextHandle); + } + + *EnumHandle = NULL; + + if (status == ERROR_NOT_CONNECTED) + { + // + // Object name not found. We should return path not found. + // + status = ERROR_PATH_NOT_FOUND; + } + + return status; +} + + +DWORD +NwrEnum( + IN NWWKSTA_CONTEXT_HANDLE EnumHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function + +Arguments: + + EnumHandle - Supplies a pointer to the context handle which identifies + what type of object we are enumerating and the string of the + container name to concatenate to the returned object. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + + WN_BAD_HANDLE - The specified enumeration handle is invalid. + +--*/ // NwrEnum +{ + DWORD status; + LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle; + BOOL fImpersonate = FALSE ; + + if (ContextHandle->Signature != NW_HANDLE_SIGNATURE) { + return WN_BAD_HANDLE; + } + + // + // Impersonate the client + // + if ((status = NwImpersonateClient()) != NO_ERROR) + { + goto CleanExit; + } + fImpersonate = TRUE ; + + *EntriesRead = 0; + *BytesNeeded = 0; + + RtlZeroMemory(Buffer, BufferSize); + + switch (ContextHandle->HandleType) { + case NwsHandleListConnections: + { + if (!(ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC)) + { + status = NwEnumerateConnections( + &ContextHandle->ResumeId, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead, + ContextHandle->ConnectionType + ); + if (status != ERROR_NO_MORE_ITEMS) + break; + else + { + // + // finished with all redir connections. look for + // symbolic ones. we got NO MORE ITEMS back, so we just + // carry one with the next set with the same buffers. + // + ContextHandle->ConnectionType |= CONNTYPE_SYMBOLIC ; + ContextHandle->ResumeId = 0 ; + } + } + + if (ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC) + { + // + // BUGBUG - This works around a weirdness in + // QueryDosDevices called by NwrEnumGWDevices. + // While impersonating the Win32 API will just fail. + // + (void) NwRevertToSelf() ; + fImpersonate = FALSE ; + + status = NwrEnumGWDevices( + NULL, + &ContextHandle->ResumeId, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead) ; + + // + // if we have more items, MPR expects success. map + // accordingly. + // + if ((status == ERROR_MORE_DATA) && *EntriesRead) + { + status = NO_ERROR ; + } + + // + // if nothing left, map to the distinguished MPR error + // + else if ((status == NO_ERROR) && (*EntriesRead == 0)) + { + status = ERROR_NO_MORE_ITEMS ; + } + break ; + } + } + + case NwsHandleListContextInfo_Tree: + case NwsHandleListContextInfo_Server: + + status = NwEnumContextInfo( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListServersAndNdsTrees: + + status = NwEnumServersAndNdsTrees( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListVolumes: + + status = NwEnumVolumes( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListNdsSubTrees_Disk: + + status = NwEnumNdsSubTrees_Disk( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + + break; + + case NwsHandleListNdsSubTrees_Print: + + status = NwEnumNdsSubTrees_Print( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + + break; + + case NwsHandleListNdsSubTrees_Any: + + status = NwEnumNdsSubTrees_Any( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + + break; + + case NwsHandleListQueues: + + status = NwEnumQueues( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListVolumesQueues: + + status = NwEnumVolumesQueues( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListDirectories: + + status = NwEnumDirectories( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + + break; + + case NwsHandleListPrintServers: + + status = NwEnumPrintServers( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + case NwsHandleListPrintQueues: + + status = NwEnumPrintQueues( + ContextHandle, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead + ); + break; + + default: + KdPrint(("NWWORKSTATION: NwrEnum unexpected handle type %lu\n", + ContextHandle->HandleType)); + ASSERT(FALSE); + status = WN_BAD_HANDLE; + goto CleanExit ; + } + + if (*EntriesRead > 0) { + + switch ( ContextHandle->HandleType ) { + case NwsHandleListConnections: + case NwsHandleListContextInfo_Tree: + case NwsHandleListContextInfo_Server: + case NwsHandleListServersAndNdsTrees: + case NwsHandleListVolumes: + case NwsHandleListQueues: + case NwsHandleListVolumesQueues: + case NwsHandleListDirectories: + case NwsHandleListNdsSubTrees_Disk: + case NwsHandleListNdsSubTrees_Any: + { + DWORD i; + LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer; + + // + // Replace pointers to strings with offsets as need + // + + if ((ContextHandle->HandleType == NwsHandleListConnections) + && (ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC)) + { + // + // NwrEnumGWDevices already return offsets. + // + break ; + } + + for (i = 0; i < *EntriesRead; i++, NetR++) { + + if (NetR->lpLocalName != NULL) { + NetR->lpLocalName = (LPWSTR) + ((DWORD) (NetR->lpLocalName) - (DWORD) Buffer); + } + + NetR->lpRemoteName = + (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD)Buffer); + + if (NetR->lpComment != NULL) { + NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - + (DWORD) Buffer); + } + + if (NetR->lpProvider != NULL) { + NetR->lpProvider = + (LPWSTR) ((DWORD) (NetR->lpProvider) - + (DWORD) Buffer); + } + } + break; + } + + case NwsHandleListPrintServers: + case NwsHandleListPrintQueues: + case NwsHandleListNdsSubTrees_Print: + { + DWORD i; + PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) Buffer; + + // + // Sort the entries in the buffer + // + if ( *EntriesRead > 1 ) + qsort( Buffer, *EntriesRead, + sizeof( PRINTER_INFO_1W ), SortFunc ); + + // + // Replace pointers to strings with offsets + // + for (i = 0; i < *EntriesRead; i++, pPrinterInfo1++) { + + MarshallDownStructure( (LPBYTE) pPrinterInfo1, + PrinterInfo1Offsets, + Buffer ); + } + break; + } + + default: + KdPrint(("NWWORKSTATION: NwrEnum (pointer to offset code) unexpected handle type %lu\n", ContextHandle->HandleType)); + ASSERT( FALSE ); + break; + } + } + +CleanExit: + + if (fImpersonate) + (void) NwRevertToSelf() ; + + return status; +} + + +DWORD +NwrEnumConnections( + IN NWWKSTA_CONTEXT_HANDLE EnumHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead, + IN DWORD fImplicitConnections + ) +/*++ + +Routine Description: + + This function is an alternate to NwrEnum. It only accepts handles + that are opened with ListConnections. This function takes a flag + indicating whether we need to show all implicit connections or not. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff ERROR_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + + fImplicitConnections - TRUE if we also want to get implicit connections, + FALSE otherwise. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + ERROR_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwrEnumConnections +{ + DWORD status; + LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle; + + if ( (ContextHandle->Signature != NW_HANDLE_SIGNATURE) + || ( ContextHandle->HandleType != NwsHandleListConnections ) + ) + { + return WN_BAD_HANDLE; + } + + *EntriesRead = 0; + *BytesNeeded = 0; + + RtlZeroMemory(Buffer, BufferSize); + + if ( fImplicitConnections ) + ContextHandle->ConnectionType |= CONNTYPE_IMPLICIT; + + status = NwEnumerateConnections( + &ContextHandle->ResumeId, + EntriesRequested, + Buffer, + BufferSize, + BytesNeeded, + EntriesRead, + ContextHandle->ConnectionType + ); + + if (*EntriesRead > 0) { + + // + // Replace pointers to strings with offsets + // + + DWORD i; + LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer; + + for (i = 0; i < *EntriesRead; i++, NetR++) { + + if (NetR->lpLocalName != NULL) { + NetR->lpLocalName = (LPWSTR) + ((DWORD) (NetR->lpLocalName) - (DWORD) Buffer); + } + + NetR->lpRemoteName = + (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD)Buffer); + + if (NetR->lpComment != NULL) { + NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - + (DWORD) Buffer); + } + + if (NetR->lpProvider != NULL) { + NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) - + (DWORD) Buffer); + } + } + } + + return status; +} + + +DWORD +NwEnumContextInfo( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all of the bindery servers that are currently + connected, then sets the context handle so that the next NPEnumResource + call goes to the NDS subtree for the user's NDS context information + (if using NDS). + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumContextInfo +{ + DWORD status = NO_ERROR; + DWORD tempResumeId = 0; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + DWORD LastObjectId = ContextHandle->ResumeId; + + while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS && + FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR ) + { + tempResumeId = ContextHandle->ResumeId; + + status = NwGetNextServerConnection( ContextHandle ); + + if ( status == NO_ERROR && ContextHandle->ResumeId != 0 ) + { + // + // Pack bindery server name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + L"\\\\", + NULL, + (LPWSTR) ContextHandle->ResumeId, // A server name + RESOURCE_CONTEXT, + RESOURCEDISPLAYTYPE_SERVER, + RESOURCEUSAGE_CONTAINER, + RESOURCETYPE_ANY, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer, + // backup ResumeId to previous entry. + // + ContextHandle->ResumeId = tempResumeId; + ContextHandle->NdsRawDataCount += 1; + + if (*EntriesRead) + { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else + { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) + { + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + } + } + else if ( status == WN_NO_MORE_ENTRIES ) + { + // + // We processed the last item in list, so + // start enumerating servers. + // + ContextHandle->ResumeId = 0; + LastObjectId = 0; + + if ( ContextHandle->HandleType == NwsHandleListContextInfo_Tree ) + { + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS; + } + } + } + + if ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS ) + { + ContextHandle->HandleType = NwsHandleListNdsSubTrees_Any; + status = NO_ERROR; + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) + { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwEnumServersAndNdsTrees( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the servers and NDS trees on the local + network by: 1) scanning the bindery for file server objects on the + preferred server and 2) scanning the bindery for directory servers + (NDS trees) on the preferred server. The server and tree entries are + returned in an array of NETRESOURCE entries; each servername is + prefixed by \\. + + The ContextHandle->ResumeId field is initially 0xffffffff before + enumeration begins and contains the object ID of the last server + or NDS tree object returned, depending on the value of + ContextHandle->dwUsingNds. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumServersAndNdsTrees +{ + DWORD status = NO_ERROR; + DWORD tempResumeId = 0; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + + SERVERNAME ServerName; // OEM server name + LPWSTR UServerName = NULL; // Unicode server name + DWORD LastObjectId = ContextHandle->ResumeId; + + while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS && + FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR ) + { + tempResumeId = ContextHandle->ResumeId; + + // + // Call the scan bindery object NCP to scan for all NDS + // tree objects. + // + status = NwGetNextNdsTreeEntry( ContextHandle ); + + if ( status == NO_ERROR && ContextHandle->ResumeId != 0 ) + { + // + // Pack tree name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + L" \\\\", + NULL, + (LPWSTR) ContextHandle->ResumeId, // This is a NDS tree name + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_TREE, + RESOURCEUSAGE_CONTAINER, + RESOURCETYPE_ANY, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer, backup ResumeId to + // previous entry. + // + ContextHandle->ResumeId = tempResumeId; + ContextHandle->NdsRawDataCount += 1; + + if (*EntriesRead) + { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else + { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) + { + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + } + } + else if ( status == WN_NO_MORE_ENTRIES ) + { + // + // We processed the last item in list, so + // start enumerating servers. + // + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + ContextHandle->ResumeId = 0xFFFFFFFF; + LastObjectId = 0xFFFFFFFF; + } + } + + if ( status == WN_NO_MORE_ENTRIES) + { + status = NO_ERROR; + } + + while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS && + FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR ) + { + RtlZeroMemory(ServerName, sizeof(ServerName)); + + // + // Call the scan bindery object NCP to scan for all file + // server objects. + // + status = NwGetNextServerEntry( + ContextHandle->TreeConnectionHandle, + &LastObjectId, + ServerName + ); + + if (status == NO_ERROR && NwConvertToUnicode(&UServerName, ServerName)) + { + // + // Pack server name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + L"\\\\", + NULL, + UServerName, + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_SERVER, + RESOURCEUSAGE_CONTAINER, + RESOURCETYPE_ANY, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) + { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else + { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) + { + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + + ContextHandle->ResumeId = LastObjectId; + } + + (void) LocalFree((HLOCAL) UServerName); + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) + { + status = NO_ERROR; + } + + return status; +} + + + +DWORD +NwEnumVolumes( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the volumes on a server by + iteratively getting the volume name for each volume number from + 0 - 31 until we run into the first volume number that does not + map to a volume name (this method assumes that volume numbers + are used contiguously in ascending order). The volume entries + are returned in an array of NETRESOURCE entries; each volume + name if prefixed by \\Server\. + + The ContextHandle->ResumeId field always indicates the next + volume entry to return. It is initially set to 0, which indicates + the first volume number to get. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumVolumes +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + + CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name + LPWSTR UVolumeName = NULL; // Unicode volume name + DWORD NextVolumeNumber = ContextHandle->ResumeId; + DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes; + + if (NextVolumeNumber == MaxVolumeNumber) { + // + // Reached the end of enumeration + // + return WN_NO_MORE_ENTRIES; + } + + while (FitInBuffer && + EntriesRequested > *EntriesRead && + NextVolumeNumber < MaxVolumeNumber && + status == NO_ERROR) { + + RtlZeroMemory(VolumeName, sizeof(VolumeName)); + + // + // Call the scan bindery object NCP to scan for all file + // volume objects. + // + status = NwGetNextVolumeEntry( + ContextHandle->TreeConnectionHandle, + NextVolumeNumber++, + VolumeName + ); + + if (status == NO_ERROR) { + + if (VolumeName[0] == 0) { + + // + // Got an empty volume name back for the next volume number + // which indicates there is no volume associated with the + // volume number but still got error success. + // + // Treat this as having reached the end of the enumeration. + // + NextVolumeNumber = MaxVolumeNumber; + ContextHandle->ResumeId = MaxVolumeNumber; + + if (*EntriesRead == 0) { + status = WN_NO_MORE_ENTRIES; + } + + } + else if (NwConvertToUnicode(&UVolumeName, VolumeName)) { + + // + // Pack volume name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + ContextHandle->ContainerName, + NULL, + UVolumeName, + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_SHARE, +#ifdef NT1057 + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_CONTAINER, +#else + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_NOLOCALDEVICE, +#endif + RESOURCETYPE_DISK, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) { + + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) { + + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + + ContextHandle->ResumeId = NextVolumeNumber; + } + + (void) LocalFree((HLOCAL) UVolumeName); + } + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwEnumNdsSubTrees_Disk( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates the sub-trees of a given NDS tree + handle. It returns the fully-qualified UNC path of the sub-tree + entries in an array of NETRESOURCE entries. + + The ContextHandle->ResumeId field is 0 initially, and contains + a pointer to the subtree name string of the last sub-tree + returned. If there are no more sub-trees to return, this + field is set to 0xffffffff. + +Arguments: + + ContextHandle - Supplies the enum context handle. It contains + an opened NDS tree handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumNdsSubTrees_Disk +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize = 0; + + DWORD SubTreeName = 0; + DWORD ResourceScope = 0; + DWORD ResourceType = 0; + DWORD ResourceDisplayType = 0; + DWORD ResourceUsage = 0; + LPWSTR StrippedObjectName = NULL; + + if (ContextHandle->ResumeId == 0xffffffff) + { + // + // Reached the end of enumeration. + // + return WN_NO_MORE_ENTRIES; + } + + while (FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR) + { + if ( ContextHandle->ResumeId == 0 ) + { + // + // Get the first subtree entry. + // + status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize ); + } + + // + // Either ResumeId contains the first entry we just got from + // NwGetFirstDirectoryEntry or it contains the next directory + // entry to return. + // + if (status == NO_ERROR && ContextHandle->ResumeId != 0) + { + BYTE ClassType; + LPWSTR newPathStr = NULL; + LPWSTR tempStr = NULL; + WORD tempStrLen; + + // + // Get current subtree data from ContextHandle + // + ClassType = NwGetSubTreeData( ContextHandle->ResumeId, + &SubTreeName, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage, + &StrippedObjectName ); + + if ( StrippedObjectName == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + switch( ClassType ) + { + case CLASS_TYPE_COUNTRY: + case CLASS_TYPE_DIRECTORY_MAP: + case CLASS_TYPE_NCP_SERVER: + case CLASS_TYPE_ORGANIZATION: + case CLASS_TYPE_ORGANIZATIONAL_UNIT: + case CLASS_TYPE_VOLUME: + + // + // Need to build a string with the new NDS UNC path for subtree object + // + newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( StrippedObjectName ) + + wcslen( ContextHandle->ContainerName ) + + 3 ) * sizeof(WCHAR) ); + + if ( newPathStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_TREE_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcscpy( newPathStr, L"\\\\" ); + wcsncat( newPathStr, tempStr, tempStrLen ); + wcscat( newPathStr, L"\\" ); + wcscat( newPathStr, StrippedObjectName ); + } + + (void) LocalFree((HLOCAL) StrippedObjectName ); + StrippedObjectName = NULL; + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_PATH_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcscat( newPathStr, L"." ); + wcsncat( newPathStr, tempStr, tempStrLen ); + } + + // + // Pack subtree name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + NULL, + newPathStr, + ResourceScope, + ResourceDisplayType, + ResourceUsage, + ResourceType, + NULL, + NULL, + &EntrySize ); + + if ( status == NO_ERROR ) + { + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + } + + if ( newPathStr ) + (void) LocalFree( (HLOCAL) newPathStr ); + + break; + + case CLASS_TYPE_ALIAS: + case CLASS_TYPE_AFP_SERVER: + case CLASS_TYPE_BINDERY_OBJECT: + case CLASS_TYPE_BINDERY_QUEUE: + case CLASS_TYPE_COMPUTER: + case CLASS_TYPE_GROUP: + case CLASS_TYPE_LOCALITY: + case CLASS_TYPE_ORGANIZATIONAL_ROLE: + case CLASS_TYPE_PRINTER: + case CLASS_TYPE_PRINT_SERVER: + case CLASS_TYPE_PROFILE: + case CLASS_TYPE_QUEUE: + case CLASS_TYPE_TOP: + case CLASS_TYPE_UNKNOWN: + case CLASS_TYPE_USER: + break; + + default: + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk - Unhandled switch statement case %lu\n", ClassType )); + ASSERT( FALSE ); + break; + } + + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) + { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else + { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) + { + // + // Get next directory entry. + // + status = NwGetNextNdsSubTreeEntry( ContextHandle ); + } + } + + if (status == WN_NO_MORE_ENTRIES) + { + ContextHandle->ResumeId = 0xffffffff; + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + +#if DBG + IF_DEBUG(ENUM) + { + KdPrint(("NwEnumNdsSubTrees_Disk returns %lu\n", status)); + } +#endif + + return status; +} + + +DWORD +NwEnumNdsSubTrees_Print( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the NDS subtree objects that are either containers, + queues, printers, or servers from a given NDS tree or subtree. The entries are + returned in an array of PRINTER_INFO_1 entries and each name is prefixed + by the parent path in NDS UNC style (ex. \\tree\CN=foo.OU=bar.O=blah). + + The ContextHandle->ResumeId field is initially 0xffffffff before + enumeration begins and contains the object ID of the last NDS object returned. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes copied or required to get all + the requested entries. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + +Return Value: + + NO_ERROR - Buffer contains all the entries requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit the requested entries. + +--*/ // NwEnumNdsSubTrees_Print +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + DWORD EntrySize; + BOOL FitInBuffer = TRUE; + + DWORD SubTreeName = 0; + DWORD ResourceScope = 0; + DWORD ResourceType = 0; + DWORD ResourceDisplayType = 0; + DWORD ResourceUsage = 0; + LPWSTR StrippedObjectName = NULL; + BYTE ClassType = 0; + LPWSTR newPathStr = NULL; + LPWSTR tempStr = NULL; + WORD tempStrLen = 0; + + while ( EntriesRequested > *EntriesRead && + ( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) + { + if (ContextHandle->ResumeId == 0) + { + // + // Get the first subtree entry. + // + status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize ); + } + + // + // Either ResumeId contains the first entry we just got from + // NwGetFirstDirectoryEntry or it contains the next directory + // entry to return. + // + if (status == NO_ERROR && ContextHandle->ResumeId != 0) + { + + // + // Get current subtree data from ContextHandle + // + ClassType = NwGetSubTreeData( ContextHandle->ResumeId, + &SubTreeName, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage, + &StrippedObjectName ); + + if ( StrippedObjectName == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + switch( ClassType ) + { + + case CLASS_TYPE_COUNTRY: + case CLASS_TYPE_ORGANIZATION: + case CLASS_TYPE_ORGANIZATIONAL_UNIT: + case CLASS_TYPE_NCP_SERVER: + case CLASS_TYPE_QUEUE: + // + // Need to build a string with the new NDS UNC path for subtree object + // + newPathStr = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( StrippedObjectName ) + + wcslen( ContextHandle->ContainerName ) + + 2 ) * sizeof(WCHAR) ); + + if ( newPathStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_TREE_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcsncpy( newPathStr, tempStr, tempStrLen ); + wcscat( newPathStr, L"\\" ); + wcscat( newPathStr, StrippedObjectName ); + } + + (void) LocalFree((HLOCAL) StrippedObjectName ); + StrippedObjectName = NULL; + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_PATH_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcscat( newPathStr, L"." ); + wcsncat( newPathStr, tempStr, tempStrLen ); + } + + switch( ClassType ) + { + case CLASS_TYPE_COUNTRY: + case CLASS_TYPE_ORGANIZATION: + case CLASS_TYPE_ORGANIZATIONAL_UNIT: + // + // Pack sub-tree container name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + newPathStr, + PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1, + &EntrySize ); + + break; + + case CLASS_TYPE_NCP_SERVER: + // + // Pack server name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + newPathStr, + PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3, + &EntrySize ); + + break; + + case CLASS_TYPE_QUEUE: + // + // Pack print server queue name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + L"\\\\", + newPathStr, + PRINTER_ENUM_ICON8, + &EntrySize ); + break; + + default: +KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType )); + ASSERT(FALSE); + break; + } + + switch ( status ) + { + case ERROR_INSUFFICIENT_BUFFER: + FitInBuffer = FALSE; + // Falls through + + case NO_ERROR: + *BytesNeeded += EntrySize; + (*EntriesRead)++; + break; + + default: + break; + } + + if ( newPathStr ) + (void) LocalFree( (HLOCAL) newPathStr ); + + break; + + case CLASS_TYPE_ALIAS: + case CLASS_TYPE_AFP_SERVER: + case CLASS_TYPE_BINDERY_OBJECT: + case CLASS_TYPE_BINDERY_QUEUE: + case CLASS_TYPE_COMPUTER: + case CLASS_TYPE_DIRECTORY_MAP: + case CLASS_TYPE_GROUP: + case CLASS_TYPE_LOCALITY: + case CLASS_TYPE_ORGANIZATIONAL_ROLE: + case CLASS_TYPE_PRINTER: + case CLASS_TYPE_PRINT_SERVER: + case CLASS_TYPE_PROFILE: + case CLASS_TYPE_TOP: + case CLASS_TYPE_UNKNOWN: + case CLASS_TYPE_USER: + case CLASS_TYPE_VOLUME: + break; + + default: +KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType )); + ASSERT( FALSE ); + break; + } + + if ( status == NO_ERROR || status == ERROR_INSUFFICIENT_BUFFER ) + { + // + // Get next directory entry. + // + status = NwGetNextNdsSubTreeEntry( ContextHandle ); + } + } + } + + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if ( !FitInBuffer ) + { + *EntriesRead = 0; + status = ERROR_INSUFFICIENT_BUFFER; + } + else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) + { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwEnumNdsSubTrees_Any( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates the sub-trees of a given NDS tree + handle. It returns the fully-qualified UNC path of ANY sub-tree + entries in an array of NETRESOURCE entries. + + The ContextHandle->ResumeId field is 0 initially, and contains + a pointer to the subtree name string of the last sub-tree + returned. If there are no more sub-trees to return, this + field is set to 0xffffffff. + +Arguments: + + ContextHandle - Supplies the enum context handle. It contains + an opened NDS tree handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumNdsSubTrees_Any +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize = 0; + + DWORD SubTreeName = 0; + DWORD ResourceScope = 0; + DWORD ResourceType = 0; + DWORD ResourceDisplayType = 0; + DWORD ResourceUsage = 0; + LPWSTR StrippedObjectName = NULL; + + if (ContextHandle->ResumeId == 0xffffffff) + { + // + // Reached the end of enumeration. + // + return WN_NO_MORE_ENTRIES; + } + + while (FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR) + { + if ( ContextHandle->ResumeId == 0 ) + { + // + // Get the first subtree entry. + // + status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize ); + } + + // + // Either ResumeId contains the first entry we just got from + // NwGetFirstDirectoryEntry or it contains the next directory + // entry to return. + // + if (status == NO_ERROR && ContextHandle->ResumeId != 0) + { + BYTE ClassType; + LPWSTR newPathStr = NULL; + LPWSTR tempStr = NULL; + WORD tempStrLen; + + // + // Get current subtree data from ContextHandle + // + ClassType = NwGetSubTreeData( ContextHandle->ResumeId, + &SubTreeName, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage, + &StrippedObjectName ); + + if ( StrippedObjectName == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + switch( ClassType ) + { + case CLASS_TYPE_COUNTRY: + case CLASS_TYPE_ORGANIZATION: + case CLASS_TYPE_ORGANIZATIONAL_UNIT: + case CLASS_TYPE_VOLUME: + case CLASS_TYPE_DIRECTORY_MAP: + case CLASS_TYPE_NCP_SERVER: + case CLASS_TYPE_QUEUE: + + // + // Need to build a string with the new NDS UNC path for subtree object + // + newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( StrippedObjectName ) + + wcslen( ContextHandle->ContainerName ) + + 3 ) * sizeof(WCHAR) ); + + if ( newPathStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n", + GetLastError())); + + return ERROR_NOT_ENOUGH_MEMORY; + } + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_TREE_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcscpy( newPathStr, L"\\\\" ); + wcsncat( newPathStr, tempStr, tempStrLen ); + wcscat( newPathStr, L"\\" ); + wcscat( newPathStr, StrippedObjectName ); + } + + (void) LocalFree((HLOCAL) StrippedObjectName ); + StrippedObjectName = NULL; + + tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr, + ContextHandle->ContainerName, + PARSE_NDS_GET_PATH_NAME ); + + tempStrLen /= sizeof( WCHAR ); + + if ( tempStrLen > 0 ) + { + wcscat( newPathStr, L"." ); + wcsncat( newPathStr, tempStr, tempStrLen ); + } + + // + // Pack subtree name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + NULL, + newPathStr, + ResourceScope, + ResourceDisplayType, + ResourceUsage, + ResourceType, + NULL, + NULL, + &EntrySize ); + + if ( status == NO_ERROR ) + { + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + } + + if ( newPathStr ) + (void) LocalFree( (HLOCAL) newPathStr ); + + break; + + case CLASS_TYPE_ALIAS: + case CLASS_TYPE_AFP_SERVER: + case CLASS_TYPE_BINDERY_OBJECT: + case CLASS_TYPE_BINDERY_QUEUE: + case CLASS_TYPE_COMPUTER: + case CLASS_TYPE_GROUP: + case CLASS_TYPE_LOCALITY: + case CLASS_TYPE_ORGANIZATIONAL_ROLE: + case CLASS_TYPE_PRINTER: + case CLASS_TYPE_PRINT_SERVER: + case CLASS_TYPE_PROFILE: + case CLASS_TYPE_TOP: + case CLASS_TYPE_UNKNOWN: + case CLASS_TYPE_USER: + break; + + default: + KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any - Unhandled switch statement case %lu\n", ClassType )); + ASSERT( FALSE ); + break; + } + + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) + { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else + { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) + { + // + // Get next directory entry. + // + status = NwGetNextNdsSubTreeEntry( ContextHandle ); + } + } + + if (status == WN_NO_MORE_ENTRIES) + { + ContextHandle->ResumeId = 0xffffffff; + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + +#if DBG + IF_DEBUG(ENUM) + { + KdPrint(("NwEnumNdsSubTrees_Any returns %lu\n", status)); + } +#endif + + return status; +} + + +DWORD +NwEnumVolumesQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the volumes and queues on a server. + The queue entries are returned in an array of NETRESOURCE entries; + each queue name is prefixed by \\Server\. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumVolumesQueues +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + + CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name + LPWSTR UVolumeName = NULL; // Unicode volume name + DWORD NextObject = ContextHandle->ResumeId; + DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes; + + while (FitInBuffer && + EntriesRequested > *EntriesRead && + ContextHandle->ConnectionType == CONNTYPE_DISK && + (NextObject >= 0 && NextObject < MaxVolumeNumber) && + status == NO_ERROR) { + + + RtlZeroMemory(VolumeName, sizeof(VolumeName)); + + // + // Call the scan bindery object NCP to scan for all file + // volume objects. + // + status = NwGetNextVolumeEntry( + ContextHandle->TreeConnectionHandle, + NextObject++, + VolumeName + ); + + if (status == NO_ERROR) { + + if (VolumeName[0] == 0) { + + // + // Got an empty volume name back for the next volume number + // which indicates there is no volume associated with the + // volume number but still got error success. + // + // Treat this as having reached the end of the enumeration. + // + NextObject = 0xFFFFFFFF; + ContextHandle->ResumeId = 0xFFFFFFFF; + ContextHandle->ConnectionType = CONNTYPE_PRINT; + + } + else if (NwConvertToUnicode(&UVolumeName, VolumeName)) { + + // + // Pack volume name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + ContextHandle->ContainerName, + NULL, + UVolumeName, + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_SHARE, +#ifdef NT1057 + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_CONTAINER, +#else + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_NOLOCALDEVICE, +#endif + RESOURCETYPE_DISK, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) { + + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) { + + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + + ContextHandle->ResumeId = NextObject; + } + + (void) LocalFree((HLOCAL) UVolumeName); + } + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) + { + status = NO_ERROR; + } + + if ( *EntriesRead == 0 && + status == NO_ERROR && + ContextHandle->ConnectionType == CONNTYPE_DISK ) + { + ContextHandle->ConnectionType = CONNTYPE_PRINT; + ContextHandle->ResumeId = 0xFFFFFFFF; + } + + // + // The user needs to be validated on a netware311 server to + // get the print queues. So, we need to close the handle and + // open a new one with WRITE access. If any error occurred while + // we are enumerating the print queues, we will abort and + // assume there are no print queues on the server. + // + + if ( FitInBuffer && + EntriesRequested > *EntriesRead && + ContextHandle->ConnectionType == CONNTYPE_PRINT && + status == NO_ERROR ) + { + UNICODE_STRING TreeConnectStr; + DWORD QueueEntriesRead = 0; + + (void) NtClose(ContextHandle->TreeConnectionHandle); + + // + // Open a tree connection handle to \Device\NwRdr\ContainerName + // + status = NwCreateTreeConnectName( + ContextHandle->ContainerName, + NULL, + &TreeConnectStr ); + + if (status != NO_ERROR) + return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES ); + + + status = NwOpenCreateConnection( + &TreeConnectStr, + NULL, + NULL, + ContextHandle->ContainerName, + FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_DATA, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + RESOURCETYPE_PRINT, // Only matters when connecting beyond servername + &ContextHandle->TreeConnectionHandle, + NULL ); + + (void) LocalFree((HLOCAL) TreeConnectStr.Buffer); + + if (status != NO_ERROR) + return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES ); + + status = NwEnumQueues( + ContextHandle, + EntriesRequested == 0xFFFFFFFF? + EntriesRequested : (EntriesRequested - *EntriesRead), + FixedPortion, + ((LPBYTE) EndOfVariableData - (LPBYTE) FixedPortion), + BytesNeeded, + &QueueEntriesRead ); + + if ( status == NO_ERROR ) + { + *EntriesRead += QueueEntriesRead; + } + else if ( *EntriesRead ) + { + // + // As long as we read something into the buffer, + // we should return success. + // + status = NO_ERROR; + *BytesNeeded = 0; + } + + } + + if ( status == NO_ERROR && + *EntriesRead == 0 && + ContextHandle->ConnectionType == CONNTYPE_PRINT ) + { + return WN_NO_MORE_ENTRIES; + } + + return status; + +} + + + +DWORD +NwEnumQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the queues on a server. + The queue entries are returned in an array of NETRESOURCE entries; + each queue name is prefixed by \\Server\. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumQueues +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + + DWORD NextObject = ContextHandle->ResumeId; + + SERVERNAME QueueName; // OEM queue name + LPWSTR UQueueName = NULL; // Unicode queue name + + while ( FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR ) { + + RtlZeroMemory(QueueName, sizeof(QueueName)); + + // + // Call the scan bindery object NCP to scan for all file + // volume objects. + // + status = NwGetNextQueueEntry( + ContextHandle->TreeConnectionHandle, + &NextObject, + QueueName + ); + + if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) { + + // + // Pack server name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + ContextHandle->ContainerName, + NULL, + UQueueName, + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_SHARE, + RESOURCEUSAGE_CONNECTABLE, + RESOURCETYPE_PRINT, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) { + + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) { + + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + + ContextHandle->ResumeId = NextObject; + } + + (void) LocalFree((HLOCAL) UQueueName); + } + } + + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwEnumDirectories( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates the directories of a given directory + handle by calling NtQueryDirectoryFile. It returns the + fully-qualified UNC path of the directory entries in an array + of NETRESOURCE entries. + + The ContextHandle->ResumeId field is 0 initially, and contains + a pointer to the directory name string of the last directory + returned. If there are no more directories to return, this + field is set to 0xffffffff. + +Arguments: + + ContextHandle - Supplies the enum context handle. It contains + an opened directory handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes required to get the + first entry. This value is returned iff WN_MORE_DATA is + the return code, and Buffer is too small to even fit one + entry. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + NO_ERROR is returned as long as at least one entry was written + into Buffer but does not necessarily mean that it's the number + of EntriesRequested. + +Return Value: + + NO_ERROR - At least one entry was written to output buffer, + irregardless of the number requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit a single entry. + +--*/ // NwEnumDirectories +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + BOOL FitInBuffer = TRUE; + DWORD EntrySize; + + if (ContextHandle->ResumeId == 0xffffffff) { + // + // Reached the end of enumeration. + // + return WN_NO_MORE_ENTRIES; + } + + while (FitInBuffer && + EntriesRequested > *EntriesRead && + status == NO_ERROR) { + + if (ContextHandle->ResumeId == 0) { + + // + // Get the first directory entry. + // + status = NwGetFirstDirectoryEntry( + ContextHandle->TreeConnectionHandle, + (LPWSTR *) &ContextHandle->ResumeId + ); + } + + // + // Either ResumeId contains the first entry we just got from + // NwGetFirstDirectoryEntry or it contains the next directory + // entry to return. + // + if (ContextHandle->ResumeId != 0) { + + // + // Pack directory name into output buffer. + // + status = NwWriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + ContextHandle->ContainerName, + NULL, + (LPWSTR) ContextHandle->ResumeId, + RESOURCE_GLOBALNET, + RESOURCEDISPLAYTYPE_SHARE, +#ifdef NT1057 + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_CONTAINER, +#else + RESOURCEUSAGE_CONNECTABLE | + RESOURCEUSAGE_NOLOCALDEVICE, +#endif + RESOURCETYPE_DISK, + NULL, + NULL, + &EntrySize + ); + + if (status == WN_MORE_DATA) { + + // + // Could not write current entry into output buffer. + // + + if (*EntriesRead) { + // + // Still return success because we got at least one. + // + status = NO_ERROR; + } + else { + *BytesNeeded = EntrySize; + } + + FitInBuffer = FALSE; + } + else if (status == NO_ERROR) { + + // + // Note that we've returned the current entry. + // + (*EntriesRead)++; + + // + // Free memory allocated to save resume point, which is + // a buffer that contains the last directory we returned. + // + if (ContextHandle->ResumeId != 0) { + (void) LocalFree((HLOCAL) ContextHandle->ResumeId); + ContextHandle->ResumeId = 0; + } + + // + // Get next directory entry. + // + status = NwGetNextDirectoryEntry( + (LPWSTR) ContextHandle->TreeConnectionHandle, + (LPWSTR *) &ContextHandle->ResumeId + ); + + } + } + + if (status == WN_NO_MORE_ENTRIES) { + ContextHandle->ResumeId = 0xffffffff; + } + } + + // + // User asked for more than there are entries. We just say that + // all is well. + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("EnumDirectories returns %lu\n", status)); + } +#endif + + return status; +} + + +DWORD +NwEnumPrintServers( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the servers and NDS tree on the local network + by scanning the bindery for file server or directory objects on the + preferred server. The server and tree entries are returned in an + array of PRINTER_INFO_1 entries; each entry name is prefixed by + \\. + + The ContextHandle->ResumeId field is initially 0xffffffff before + enumeration begins and contains the object ID of the last server + object returned. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes copied or required to get all + the requested entries. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + +Return Value: + + NO_ERROR - Buffer contains all the entries requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit the requested entries. + +--*/ // NwEnumPrintServers +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + DWORD EntrySize; + BOOL FitInBuffer = TRUE; + + SERVERNAME ServerName; // OEM server name + LPWSTR UServerName = NULL; // Unicode server name + DWORD LastObjectId = ContextHandle->ResumeId; + WCHAR TempBuffer[50]; + + while ( EntriesRequested > *EntriesRead && + ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS && + ((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) + { + // + // Call the scan bindery object NCP to scan for all NDS + // tree objects. + // + status = NwGetNextNdsTreeEntry( ContextHandle ); + + if ( status == NO_ERROR && ContextHandle->ResumeId != 0 ) + { + // + // Put tree name into a buffer prefixed with a ' ' <space> character + // so that PrintMan displays trees as a group before servers. + // + RtlZeroMemory( TempBuffer, 50 ); + wcscpy( TempBuffer, L" " ); + wcscat( TempBuffer, (LPWSTR) ContextHandle->ResumeId ); + + // + // Pack server name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + TempBuffer, // This is a NDS tree name + PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1, + &EntrySize + ); + + switch ( status ) + { + case ERROR_INSUFFICIENT_BUFFER: + FitInBuffer = FALSE; + // Falls through + + case NO_ERROR: + *BytesNeeded += EntrySize; + (*EntriesRead)++; + // ContextHandle->ResumeId = LastObjectId; + break; + + default: + break; + } + } + else if ( status == WN_NO_MORE_ENTRIES ) + { + // + // We processed the last item in list, so + // start enumerating servers. + // + ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS; + ContextHandle->ResumeId = 0xFFFFFFFF; + LastObjectId = 0xFFFFFFFF; + } + } + + status = NO_ERROR; + + while ( EntriesRequested > *EntriesRead && + ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS && + ((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) { + + RtlZeroMemory(ServerName, sizeof(ServerName)); + + // + // Call the scan bindery object NCP to scan for all file + // server objects. + // + status = NwGetNextServerEntry( + ContextHandle->TreeConnectionHandle, + &LastObjectId, + ServerName + ); + + if (status == NO_ERROR && NwConvertToUnicode(&UServerName,ServerName)) { + + // + // Pack server name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + NULL, + UServerName, + PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3, + &EntrySize + ); + + switch ( status ) + { + case ERROR_INSUFFICIENT_BUFFER: + FitInBuffer = FALSE; + // Falls through + + case NO_ERROR: + *BytesNeeded += EntrySize; + (*EntriesRead)++; + ContextHandle->ResumeId = LastObjectId; + break; + + default: + break; + } + + (void) LocalFree((HLOCAL) UServerName); + } + } + + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if ( !FitInBuffer ) { + *EntriesRead = 0; + status = ERROR_INSUFFICIENT_BUFFER; + } + else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwEnumPrintQueues( + IN LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD EntriesRequested, + OUT LPBYTE Buffer, + IN DWORD BufferSize, + OUT LPDWORD BytesNeeded, + OUT LPDWORD EntriesRead + ) +/*++ + +Routine Description: + + This function enumerates all the print queues on a server by scanning + the bindery on the server for print queues objects. + The print queues entries are returned in an array of PRINTER_INFO_1 entries + and each printer name is prefixed by \\Server\. + + The ContextHandle->ResumeId field is initially 0xffffffff before + enumeration begins and contains the object ID of the last print queue + object returned. + +Arguments: + + ContextHandle - Supplies the enum context handle. + + EntriesRequested - Supplies the number of entries to return. If + this value is 0xffffffff, return all available entries. + + Buffer - Receives the entries we are listing. + + BufferSize - Supplies the size of the output buffer. + + BytesNeeded - Receives the number of bytes copied or required to get all + the requested entries. + + EntriesRead - Receives the number of entries returned in Buffer. + This value is only returned iff NO_ERROR is the return code. + +Return Value: + + NO_ERROR - Buffer contains all the entries requested. + + WN_NO_MORE_ENTRIES - No entries left to return. + + WN_MORE_DATA - The buffer was too small to fit the requested entries. + +--*/ // NwEnumPrintQueues +{ + DWORD status = NO_ERROR; + + LPBYTE FixedPortion = Buffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD)); + + DWORD EntrySize; + BOOL FitInBuffer = TRUE; + + SERVERNAME QueueName; // OEM queue name + LPWSTR UQueueName = NULL; // Unicode queue name + DWORD LastObjectId = ContextHandle->ResumeId; + + while ( EntriesRequested > *EntriesRead && + ( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) { + + RtlZeroMemory(QueueName, sizeof(QueueName)); + + // + // Call the scan bindery object NCP to scan for all file + // volume objects. + // + status = NwGetNextQueueEntry( + ContextHandle->TreeConnectionHandle, + &LastObjectId, + QueueName + ); + + if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) { + + // + // Pack server name into output buffer. + // + status = NwWritePrinterInfoEntry( + &FixedPortion, + &EndOfVariableData, + ContextHandle->ContainerName, + UQueueName, + PRINTER_ENUM_ICON8, + &EntrySize + ); + + switch ( status ) + { + case ERROR_INSUFFICIENT_BUFFER: + FitInBuffer = FALSE; + // Falls through + + case NO_ERROR: + *BytesNeeded += EntrySize; + (*EntriesRead)++; + ContextHandle->ResumeId = LastObjectId; + break; + + default: + break; + } + + (void) LocalFree((HLOCAL) UQueueName); + } + } + + // + // This is incompliance with the wierd provider API definition where + // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and + // at least one entry fit into output buffer, there's no telling if + // the buffer was too small for more entries or there are no more + // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES + // before knowing that the last call had actually reached the end of list. + // + if ( !FitInBuffer ) { + *EntriesRead = 0; + status = ERROR_INSUFFICIENT_BUFFER; + } + else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) { + status = NO_ERROR; + } + + return status; +} + + +DWORD +NwrCloseEnum( + IN OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function closes an enum context handle. + +Arguments: + + EnumHandle - Supplies a pointer to the enum context handle. + +Return Value: + + WN_BAD_HANDLE - Handle is not recognizable. + + NO_ERROR - Call was successful. + +--*/ // NwrCloseEnum +{ + + LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) *EnumHandle; + DWORD status = NO_ERROR ; + +#if DBG + IF_DEBUG(ENUM) + { + KdPrint(("\nNWWORKSTATION: NwrCloseEnum\n")); + } +#endif + + if (ContextHandle->Signature != NW_HANDLE_SIGNATURE) + { + ASSERT(FALSE); + return WN_BAD_HANDLE; + } + + // + // Resume handle for listing directories is a buffer which contains + // the last directory returned. + // + if (ContextHandle->HandleType == NwsHandleListDirectories && + ContextHandle->ResumeId != 0 && + ContextHandle->ResumeId != 0xFFFFFFFF) + { + (void) LocalFree((HLOCAL) ContextHandle->ResumeId); + } + + // + // NdsRawDataBuffer handle for listing NDS tree subordinates is a buffer which contains + // the last data chunk returned from redirector. + // + if ( ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk || + ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print || + ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any || + ContextHandle->HandleType == NwsHandleListServersAndNdsTrees ) && + ContextHandle->NdsRawDataBuffer ) + { + (void) LocalFree((HLOCAL) ContextHandle->NdsRawDataBuffer); + ContextHandle->NdsRawDataBuffer = 0; + } + + if (ContextHandle->TreeConnectionHandle != (HANDLE) NULL) + { + if (ContextHandle->HandleType == NwsHandleListDirectories) + { + // + // Delete the UNC connection created so that we can browse + // directories. + // + (void) NwNukeConnection(ContextHandle->TreeConnectionHandle, TRUE); + } + + if ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk || + ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print || + ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any ) + { + // + // Get rid of the connection to the NDS tree. + // + (void) CloseHandle(ContextHandle->TreeConnectionHandle); + ContextHandle->TreeConnectionHandle = 0; + } + else + { + (void) NtClose(ContextHandle->TreeConnectionHandle); + ContextHandle->TreeConnectionHandle = 0; + } + } + + ContextHandle->Signature = 0x0BADBAD0; + (void) LocalFree((HLOCAL) ContextHandle); + + *EnumHandle = NULL; + + return status; +} + + +DWORD +NwrGetUser( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR lpName, + OUT LPBYTE lpUserName, + IN DWORD dwUserNameBufferSize, + OUT LPDWORD lpdwCharsRequired + ) +/*++ + +Routine Description: + + This is used to determine either the current default username, or the + username used to establish a network connection. + +Arguments: + + Reserved - Unused. + + lpName - The connection for which user information is requested. + + lpUserName - The buffer to receive the user name associated with the + connection referred to by lpName. + + dwUserNameLen - The size of the buffer lpUserName. + + lpdwCharsRequired - If return status is WN_MORE_DATA, then this is set to + the value which indicates the number of characters that the buffer + lpUserName must hold. Otherwise, this is not set. + + +Return Value: + + WN_SUCCESS - If the call is successful. Otherwise, an error code is, + returned, which may include: + + WN_NOT_CONNECTED - lpName not a redirected device nor a connected network + name. + + WN_MORE_DATA - The buffer is too small. + +--*/ // NwrGetUser +{ + DWORD status = NO_ERROR; + WCHAR lpTempUserName[512]; + WCHAR lpTempHostName[512]; + + status = NwGetConnectionInformation( lpName, lpTempUserName, lpTempHostName ); + + if ( status != NO_ERROR ) + { + return status; + } + + if ( ( ( wcslen( lpTempUserName ) + 1 ) * sizeof(WCHAR) ) > dwUserNameBufferSize ) + { + *lpdwCharsRequired = wcslen( lpTempUserName ) + 1; + return WN_MORE_DATA; + } + + wcscpy( (LPWSTR) lpUserName, lpTempUserName ); + + return WN_SUCCESS; +} + + +DWORD +NwrGetResourceInformation( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR lpRemoteName, + IN DWORD dwType, + OUT LPBYTE lpBuffer, + IN DWORD dwBufferSize, + OUT LPDWORD lpdwBytesNeeded, + OUT LPDWORD lpdwSystemOffset + ) +/*++ + +Routine Description: + + This function returns an object which details information + about a specified network resource. + +Arguments: + + Reserved - Unused. + lpRemoteName - The full path name to be verified. + dwType - The type of the value, if the calling client knows it. + lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry. + dwBufferSize - The size of the buffer. + lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned. + lpdwSystemOffset - A DWORD that is an offset value to the beginning of a + string that specifies the part of the resource that is accessed through + resource type specific APIs rather than WNet APIs. The string is stored + in the same buffer as the returned NETRESOURCE structure, lpBuffer. + +Return Value: + + WN_SUCCESS - If the call is successful. + + WN_MORE_DATA - If input buffer is too small. + + WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination + of parameters is specified (e.g. lpRemoteName does not correspond + to dwType). + + WN_BAD_NETNAME - The resource is not recognized by this provider. + +--*/ // NwrGetResourceInformation +{ + DWORD status = NO_ERROR; + DWORD EntrySize; + + LPBYTE FixedPortion = lpBuffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD)); + LPWSTR lpObjectPathName = NULL; + LPWSTR lpSystemPathPart = NULL; + LPWSTR lpSystem = NULL; + DWORD ClassType; + DWORD ResourceScope; + DWORD ResourceType; + DWORD ResourceDisplayType; + DWORD ResourceUsage; + BOOL fReturnBadNetName = FALSE; + + *lpdwSystemOffset = 0; + + status = NwGetNDSPathInfo( lpRemoteName, + &lpObjectPathName, + &lpSystemPathPart, + &ClassType, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage ); + + if ( status == VERIFY_ERROR_NOT_A_NDS_TREE ) + { + // + // Code to handle \\SERVER\VOL\... here! + // + status = NwGetBinderyPathInfo( lpRemoteName, + &lpObjectPathName, + &lpSystemPathPart, + &ClassType, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage ); + } + + if ( status == VERIFY_ERROR_PATH_NOT_FOUND ) + { + fReturnBadNetName = TRUE; + status = NO_ERROR; + } + + if ( status == NO_ERROR && + dwType != RESOURCETYPE_ANY && + ResourceType != RESOURCETYPE_ANY && + dwType != ResourceType ) + { + status = WN_BAD_VALUE; + } + + if ( status == NO_ERROR ) + { + // + // Pack subtree name into output buffer. + // + status = NwWriteNetResourceEntry( &FixedPortion, + &EndOfVariableData, + NULL, + NULL, + lpObjectPathName == NULL ? NwProviderName : lpObjectPathName, + ResourceScope, + ResourceDisplayType, + ResourceUsage, + ResourceType, + lpSystemPathPart, + &lpSystem, + &EntrySize ); + + if ( lpObjectPathName ) + (void) LocalFree( (HLOCAL) lpObjectPathName ); + } + else + { + if ( lpSystemPathPart != NULL ) + { + (void) LocalFree( (HLOCAL) lpSystemPathPart ); + lpSystemPathPart = NULL; + } + + return status; + } + + if ( status != NO_ERROR ) + { + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer. + // + *lpdwBytesNeeded = EntrySize; + } + + if ( lpSystemPathPart != NULL ) + { + (void) LocalFree( (HLOCAL) lpSystemPathPart ); + lpSystemPathPart = NULL; + } + + if ( fReturnBadNetName ) + return WN_BAD_NETNAME; + + return status; + } + else + { + LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer; + + // + // Replace pointers to strings with offsets as need + // + + if (NetR->lpLocalName != NULL) + { + NetR->lpLocalName = (LPWSTR) ((DWORD) (NetR->lpLocalName) - (DWORD) lpBuffer); + } + + NetR->lpRemoteName = (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD) lpBuffer); + + if (NetR->lpComment != NULL) + { + NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - (DWORD) lpBuffer); + } + + if (NetR->lpProvider != NULL) + { + NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) - (DWORD) lpBuffer); + } + + if (lpSystem != NULL) + { + *lpdwSystemOffset = (DWORD) lpSystem - (DWORD) lpBuffer; + } + + if ( lpSystemPathPart != NULL ) + { + (void) LocalFree( (HLOCAL) lpSystemPathPart ); + lpSystemPathPart = NULL; + } + + if ( fReturnBadNetName ) + return WN_BAD_NETNAME; + + return WN_SUCCESS; + } +} + + +DWORD +NwrGetResourceParent( + IN LPWSTR Reserved OPTIONAL, + IN LPWSTR lpRemoteName, + IN DWORD dwType, + OUT LPBYTE lpBuffer, + IN DWORD dwBufferSize, + OUT LPDWORD lpdwBytesNeeded + ) +/*++ + +Routine Description: + + This function returns an object which details information + about the parent of a specified network resource. + +Arguments: + + Reserved - Unused. + lpRemoteName - The full path name of object to find the parent of. + dwType - The type of the value, if the calling client knows it. + lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry. + dwBufferSize - The size of the buffer. + lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned. + +Return Value: + + WN_SUCCESS - If the call is successful. + + WN_MORE_DATA - If input buffer is too small. + + WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination + of parameters is specified (e.g. lpRemoteName does not correspond + to dwType). + +--*/ // NwrGetResourceParent +{ + DWORD status = NO_ERROR; + DWORD EntrySize; + + LPBYTE FixedPortion = lpBuffer; + LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion + + ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD)); + LPWSTR lpRemoteNameParent = NULL; + LPWSTR lpFullObjectPathName = NULL; + DWORD ClassType; + DWORD ResourceScope; + DWORD ResourceType; + DWORD ResourceDisplayType; + DWORD ResourceUsage; + BOOL fReturnBadNetName = FALSE; + + if ( ! NwGetRemoteNameParent( lpRemoteName, &lpRemoteNameParent ) ) + { + return WN_BAD_NETNAME; + } + + status = NwVerifyNDSObject( lpRemoteNameParent, + &lpFullObjectPathName, + &ClassType, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage ); + + if ( status == VERIFY_ERROR_NOT_A_NDS_TREE ) + { + status = NwVerifyBinderyObject( lpRemoteNameParent, + &lpFullObjectPathName, + &ClassType, + &ResourceScope, + &ResourceType, + &ResourceDisplayType, + &ResourceUsage ); + } + + if ( lpRemoteNameParent ) + (void) LocalFree( (HLOCAL) lpRemoteNameParent ); + + if ( status == VERIFY_ERROR_PATH_NOT_FOUND ) + { + fReturnBadNetName = TRUE; + status = NO_ERROR; + } + + if ( status == NO_ERROR ) + { + // + // Pack subtree name into output buffer. + // + status = NwWriteNetResourceEntry( &FixedPortion, + &EndOfVariableData, + NULL, + NULL, + lpFullObjectPathName == NULL ? NwProviderName : lpFullObjectPathName, + ResourceScope, + ResourceDisplayType, + ResourceUsage, + ResourceType, + NULL, + NULL, + &EntrySize ); + + if ( lpFullObjectPathName ) + (void) LocalFree( (HLOCAL) lpFullObjectPathName ); + } + else + { + return status; + } + + if ( status != NO_ERROR ) + { + if (status == WN_MORE_DATA) + { + // + // Could not write current entry into output buffer. + // + *lpdwBytesNeeded = EntrySize; + } + + if ( fReturnBadNetName ) + return WN_BAD_NETNAME; + + return status; + } + else + { + LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer; + + // + // Replace pointers to strings with offsets as need + // + + if (NetR->lpLocalName != NULL) + { + NetR->lpLocalName = (LPWSTR) ((DWORD) (NetR->lpLocalName) - (DWORD) lpBuffer); + } + + NetR->lpRemoteName = (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD) lpBuffer); + + if (NetR->lpComment != NULL) + { + NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - (DWORD) lpBuffer); + } + + if (NetR->lpProvider != NULL) + { + NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) - (DWORD) lpBuffer); + } + + if ( fReturnBadNetName ) + return WN_BAD_NETNAME; + + return WN_SUCCESS; + } +} + + +VOID +NWWKSTA_CONTEXT_HANDLE_rundown( + IN NWWKSTA_CONTEXT_HANDLE EnumHandle + ) +/*++ + +Routine Description: + + This function is called by RPC when a client terminates with an + opened handle. This allows us to clean up and deallocate any context + data associated with the handle. + +Arguments: + + EnumHandle - Supplies the handle opened for an enumeration. + +Return Value: + + None. + +--*/ +{ + // + // Call our close handle routine. + // + NwrCloseEnum(&EnumHandle); +} + + +DWORD +NwGetFirstNdsSubTreeEntry( + OUT LPNW_ENUM_CONTEXT ContextHandle, + IN DWORD BufferSize + ) +/*++ + +Routine Description: + + This function is called by NwEnumNdsSubTrees to get the first + subtree entry given a handle to a NDS tree. It allocates + the output buffer to hold the returned subtree name; the + caller should free this output buffer with LocalFree when done. + +Arguments: + +Return Value: + + NO_ERROR - The operation was successful. + + ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output + buffer. + + Other errors from NwNdsList. + +--*/ // NwGetFirstNdsSubTreeEntry +{ + NTSTATUS ntstatus; + + ContextHandle->NdsRawDataSize = BufferSize; + + // + // Determine size of NDS raw data buffer to use. + // + if ( ContextHandle->NdsRawDataSize < EIGHT_KB ) + ContextHandle->NdsRawDataSize = EIGHT_KB; + + // + // Create NDS raw data buffer. + // + ContextHandle->NdsRawDataBuffer = (DWORD) + LocalAlloc( LMEM_ZEROINIT, + ContextHandle->NdsRawDataSize ); + + if ( ContextHandle->NdsRawDataBuffer == 0 ) + { + KdPrint(("NWWORKSTATION: NwGetFirstNdsSubTreeEntry LocalAlloc Failed %lu\n", GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Set up to get initial NDS subordinate list. + // + ContextHandle->NdsRawDataId = INITIAL_ITERATION; + + ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle, + ContextHandle->dwOid, + &ContextHandle->NdsRawDataId, + (LPBYTE) ContextHandle->NdsRawDataBuffer, + ContextHandle->NdsRawDataSize ); + + // + // If error, clean up the ContextHandle and return. + // + if ( ntstatus != STATUS_SUCCESS || + ((PNDS_RESPONSE_SUBORDINATE_LIST) + ContextHandle->NdsRawDataBuffer)->SubordinateEntries == 0 ) + { + if ( ContextHandle->NdsRawDataBuffer ) + (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); + ContextHandle->NdsRawDataBuffer = 0; + ContextHandle->NdsRawDataSize = 0; + ContextHandle->NdsRawDataId = INITIAL_ITERATION; + ContextHandle->NdsRawDataCount = 0; + ContextHandle->ResumeId = 0; + + return WN_NO_MORE_ENTRIES; + } + + ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST) + ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1; + + ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer + + sizeof( NDS_RESPONSE_SUBORDINATE_LIST ); + + return RtlNtStatusToDosError(ntstatus); +} + + +DWORD +NwGetNextNdsSubTreeEntry( + OUT LPNW_ENUM_CONTEXT ContextHandle + ) +/*++ + +Routine Description: + + This function is called by NwEnumNdsSubTrees to get the next + NDS subtree entry given a handle to a NDS tree. It allocates + the output buffer to hold the returned subtree name; the + caller should free this output buffer with LocalFree when done. + +Arguments: + +Return Value: + + NO_ERROR - The operation was successful. + + ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output + buffer. + + Other errors from NwNdsList. + +--*/ // NwGetNextDirectoryEntry +{ + NTSTATUS ntstatus = STATUS_SUCCESS; + PBYTE pbRaw; + DWORD dwStrLen; + + + if ( ContextHandle->NdsRawDataCount == 0 && + ContextHandle->NdsRawDataId == INITIAL_ITERATION ) + return WN_NO_MORE_ENTRIES; + + if ( ContextHandle->NdsRawDataCount == 0 && + ContextHandle->NdsRawDataId != INITIAL_ITERATION ) + { + ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle, + ContextHandle->dwOid, + &ContextHandle->NdsRawDataId, + (LPBYTE) ContextHandle->NdsRawDataBuffer, + ContextHandle->NdsRawDataSize ); + + // + // If error, clean up the ContextHandle and return. + // + if (ntstatus != STATUS_SUCCESS) + { + if ( ContextHandle->NdsRawDataBuffer ) + (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer ); + ContextHandle->NdsRawDataBuffer = 0; + ContextHandle->NdsRawDataSize = 0; + ContextHandle->NdsRawDataId = INITIAL_ITERATION; + ContextHandle->NdsRawDataCount = 0; + + return WN_NO_MORE_ENTRIES; + } + + ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST) + ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1; + + ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer + + sizeof( NDS_RESPONSE_SUBORDINATE_LIST ); + + return RtlNtStatusToDosError(ntstatus); + } + + ContextHandle->NdsRawDataCount--; + + // + // Move pointer past the fixed header portion of a NDS_RESPONSE_SUBORDINATE_ENTRY + // + pbRaw = (BYTE *) ContextHandle->ResumeId; + pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY ); + + // + // Move pointer past the length value of the Class Name string + // of a NDS_RESPONSE_SUBORDINATE_ENTRY + // + dwStrLen = * (DWORD *) pbRaw; + pbRaw += sizeof( DWORD ); + + // + // Move pointer past the Class Name string of a NDS_RESPONSE_SUBORDINATE_ENTRY + // + pbRaw += ROUNDUP4( dwStrLen ); + + // + // Move pointer past the length value of the Object Name string + // of a NDS_RESPONSE_SUBORDINATE_ENTRY + // + dwStrLen = * (DWORD *) pbRaw; + pbRaw += sizeof( DWORD ); + + ContextHandle->ResumeId = (DWORD) ( pbRaw + ROUNDUP4( dwStrLen ) ); + + return RtlNtStatusToDosError(ntstatus); +} + + +BYTE +NwGetSubTreeData( + IN DWORD NdsRawDataPtr, + OUT LPDWORD SubTreeName, + OUT LPDWORD ResourceScope, + OUT LPDWORD ResourceType, + OUT LPDWORD ResourceDisplayType, + OUT LPDWORD ResourceUsage, + OUT LPWSTR * StrippedObjectName + ) +/*++ + +Routine Description: + + This function is called by NwEnumNdsSubTrees to get the information + needed to describe a single NETRESOURCE from an entry in the + NdsRawDataBuffer. + +Arguments: + + NdsRawDataPtr - Supplies the pointer to a buffer with the NDS raw data. + + SubTreeName - Receives a pointer to the returned subtree object name + found in buffer. + + ResourceScope - Receives the value of the scope for the subtree object + found in buffer. + + ResourceType - Receives the value of the type for the subtree object + found in buffer. + + ResourceDisplayType - Receives the value of the display type for the + subtree object found in buffer. + + ResourceUsage - Receives the value of the usage for the subtree object + found in buffer. + + StrippedObjectName - A pointer to receive the address of a buffer which + will contain the formatted object name. Callee must + free buffer with LocalFree(). + +Return Value: + + A DWORD with a value that is used to represent NDS object class type.. + +--*/ // NwGetSubTreeData +{ + PNDS_RESPONSE_SUBORDINATE_ENTRY pSubEntry = + (PNDS_RESPONSE_SUBORDINATE_ENTRY) NdsRawDataPtr; + PBYTE pbRaw; + DWORD dwStrLen; + LPWSTR ClassNameStr; + + pbRaw = (BYTE *) pSubEntry; + + // + // The structure of a NDS_RESPONSE_SUBORDINATE_ENTRY consists of 4 DWORDs + // followed by two standard NDS format UNICODE strings. Below we jump pbRaw + // into the buffer, past the 4 DWORDs. + // + pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY ); + + // + // Now we get the length of the first string (Base Class). + // + dwStrLen = * (DWORD *) pbRaw; + + // + // Now we point pbRaw to the first WCHAR of the first string (Base Class). + // + pbRaw += sizeof( DWORD ); + + ClassNameStr = (LPWSTR) pbRaw; + + // + // Move pbRaw into the buffer, past the first UNICODE string (WORD aligned) + // + pbRaw += ROUNDUP4( dwStrLen ); + + // + // Now we get the length of the second string (Entry Name). + // + dwStrLen = * (DWORD *) pbRaw; + + // + // Now we point pbRaw to the first WCHAR of the second string (Entry Name). + // + pbRaw += sizeof( DWORD ); + + *SubTreeName = (DWORD) pbRaw; + + // + // Strip off any CN= stuff from the object name. + // + NwStripNdsUncName( (LPWSTR) *SubTreeName, StrippedObjectName ); + + *ResourceScope = RESOURCE_GLOBALNET; + + if ( !wcscmp( ClassNameStr, CLASS_NAME_ALIAS ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_ALIAS; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_AFP_SERVER ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_AFP_SERVER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_OBJECT ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_BINDERY_OBJECT; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_QUEUE ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_BINDERY_QUEUE; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_COMPUTER ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_COMPUTER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_COUNTRY ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER; + *ResourceUsage = RESOURCEUSAGE_CONTAINER; + + return CLASS_TYPE_COUNTRY; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_DIRECTORY_MAP ) ) + { + *ResourceType = RESOURCETYPE_DISK; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE; +#ifdef NT1057 + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER; +#else + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE; +#endif + + return CLASS_TYPE_DIRECTORY_MAP; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_GROUP ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GROUP; + *ResourceUsage = 0; + + return CLASS_TYPE_GROUP; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_LOCALITY ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_LOCALITY; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_NCP_SERVER ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER; + *ResourceUsage = RESOURCEUSAGE_CONTAINER; + + return CLASS_TYPE_NCP_SERVER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATION ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER; + *ResourceUsage = RESOURCEUSAGE_CONTAINER; + + return CLASS_TYPE_ORGANIZATION; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_ROLE ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_ORGANIZATIONAL_ROLE; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_UNIT ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER; + *ResourceUsage = RESOURCEUSAGE_CONTAINER; + + return CLASS_TYPE_ORGANIZATIONAL_UNIT; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINTER ) ) + { + *ResourceType = RESOURCETYPE_PRINT; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE; + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE; + + return CLASS_TYPE_PRINTER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINT_SERVER ) ) + { + *ResourceType = RESOURCETYPE_PRINT; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER; + *ResourceUsage = RESOURCEUSAGE_CONTAINER; + + return CLASS_TYPE_PRINT_SERVER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_PROFILE ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_PROFILE; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_QUEUE ) ) + { + *ResourceType = RESOURCETYPE_PRINT; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE; + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE; + + return CLASS_TYPE_QUEUE; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_TOP ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_TOP; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_USER ) ) + { + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_USER; + } + + if ( !wcscmp( ClassNameStr, CLASS_NAME_VOLUME ) ) + { + *ResourceType = RESOURCETYPE_DISK; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE; +#ifdef NT1057 + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER; +#else + *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE; +#endif + + return CLASS_TYPE_VOLUME; + } + + // + // Otherwise if ClassNameStr is something other than Unknown, report it + // + if ( wcscmp( ClassNameStr, CLASS_NAME_UNKNOWN ) ) + { + KdPrint(("NWWORKSTATION: NwGetSubTreeData failed to recognize")); + KdPrint((" ClassName: %S\n", ClassNameStr)); + KdPrint((" Setting object attributes to Unknown for now . . .\n")); + } + + *ResourceType = RESOURCETYPE_ANY; + *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC; + *ResourceUsage = 0; + + return CLASS_TYPE_UNKNOWN; +} + + +VOID +NwStripNdsUncName( + IN LPWSTR ObjectName, + OUT LPWSTR * StrippedObjectName + ) +{ + WORD slashCount; + BOOL isNdsUnc; + LPWSTR FourthSlash; + LPWSTR TreeName; + LPWSTR ObjectPath; + DWORD TreeNameLen; + DWORD ObjectPathLen; + DWORD PrefixBytes; + DWORD CurrentPathIndex; + DWORD StrippedNameLen; + DWORD StrippedNameMaxLen = MAX_NDS_NAME_CHARS; + WCHAR StrippedName[MAX_NDS_NAME_CHARS]; + + *StrippedObjectName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, + (wcslen(ObjectName) + 1) * + sizeof(WCHAR) ); + + if ( *StrippedObjectName == NULL ) + { + return; + } + + NwpGetUncInfo( ObjectName, &slashCount, &isNdsUnc, &FourthSlash ); + + if ( slashCount >= 2 ) + { + TreeNameLen = NwParseNdsUncPath( &TreeName, + ObjectName, + PARSE_NDS_GET_TREE_NAME ); + + TreeNameLen /= sizeof(WCHAR); + + wcscpy( *StrippedObjectName, L"\\\\" ); + wcsncat( *StrippedObjectName, TreeName, TreeNameLen ); + + ObjectPathLen = NwParseNdsUncPath( &ObjectPath, + ObjectName, + PARSE_NDS_GET_PATH_NAME ); + + if ( ObjectPathLen == 0 ) + { + _wcsupr( *StrippedObjectName ); + + return; + } + + wcscat( *StrippedObjectName, L"\\" ); + } + else + { + wcscpy( *StrippedObjectName, L"" ); + + ObjectPath = ObjectName; + ObjectPathLen = wcslen(ObjectName) * sizeof(WCHAR); + } + + CurrentPathIndex = 0; + PrefixBytes = 0; + StrippedNameLen = 0; + + // + // All of these indexes are in BYTES, not WCHARS! + // + while ( ( CurrentPathIndex < ObjectPathLen ) && + ( StrippedNameLen < StrippedNameMaxLen ) ) + { + if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'=' ) + { + CurrentPathIndex += sizeof( WCHAR ); + StrippedNameLen -= PrefixBytes; + PrefixBytes = 0; + + continue; + } + + StrippedName[StrippedNameLen / sizeof( WCHAR )] = + ObjectPath[CurrentPathIndex / sizeof( WCHAR )]; + + StrippedNameLen += sizeof( WCHAR ); + CurrentPathIndex += sizeof( WCHAR ); + + if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'.' ) + { + PrefixBytes = 0; + PrefixBytes -= sizeof( WCHAR ); + } + else + { + PrefixBytes += sizeof( WCHAR ); + } + } + + StrippedName[StrippedNameLen / sizeof( WCHAR )] = L'\0'; + + wcscat( *StrippedObjectName, StrippedName ); + _wcsupr( *StrippedObjectName ); +} + + +DWORD +NwVerifyNDSObject( + IN LPWSTR lpNDSObjectNamePath, + OUT LPWSTR * lpFullNDSObjectNamePath, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ) +{ + DWORD status = NO_ERROR; + NTSTATUS ntstatus = STATUS_SUCCESS; + UNICODE_STRING TreeServerName; + UNICODE_STRING PathString; + HANDLE ConnectionHandle = NULL; + DWORD dwHandleType; + DWORD dwOid; + BOOL fImpersonate = FALSE ; + + if ( lpNDSObjectNamePath == NULL ) + { + // + // Handle this as if we are at the root of our provider hierarchy. + // + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = RESOURCETYPE_ANY; +#ifdef NT1057 + *lpResourceDisplayType = 0; +#else + *lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK; +#endif + *lpResourceUsage = RESOURCEUSAGE_CONTAINER; + + *lpFullNDSObjectNamePath = NULL; + + return NO_ERROR; + } + + TreeServerName.Buffer = NULL; + PathString.Buffer = NULL; + TreeServerName.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR ); + PathString.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR ); + + TreeServerName.Length = NwParseNdsUncPath( (LPWSTR *) &TreeServerName.Buffer, + lpNDSObjectNamePath, + PARSE_NDS_GET_TREE_NAME ); + + if ( TreeServerName.Length == 0 || TreeServerName.Buffer == NULL ) + { + // + // lpNDSObjectNamePath is not in the form \\name[\blah.blah.blah][\foo][\bar]... + // + status = WN_BAD_NETNAME; + goto ErrorExit; + } + + // + // Impersonate the client + // + if ( ( status = NwImpersonateClient() ) != NO_ERROR ) + { + goto ErrorExit; + } + + fImpersonate = TRUE; + + // + // Open a connection handle to \\name + // + ntstatus = NwNdsOpenGenericHandle( &TreeServerName, + &dwHandleType, + &ConnectionHandle ); + + if ( ntstatus != STATUS_SUCCESS ) + { + // + // The first part of lpNDSObjectNamePath was neither a NDS tree nor a NCP Server. + // + status = WN_BAD_NETNAME; + goto ErrorExit; + } + + if ( dwHandleType != HANDLE_TYPE_NDS_TREE ) + { + // + // The first part of lpNDSObjectNamePath was not a NDS tree. + // + status = VERIFY_ERROR_NOT_A_NDS_TREE; + goto ErrorExit; + } + + // + // Adjust TreeServerName.Length to number of characters. + // + TreeServerName.Length /= sizeof(WCHAR); + + // + // The lpNDSObjectNamePath points to a NDS tree. Now verify that the path is valid. + // + PathString.Length = NwParseNdsUncPath( (LPWSTR *) &PathString.Buffer, + lpNDSObjectNamePath, + PARSE_NDS_GET_PATH_NAME ); + + if ( PathString.Length == 0 ) + { + LPWSTR treeNameStr = NULL; + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( ConnectionHandle ) + CloseHandle( ConnectionHandle ); + + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = RESOURCETYPE_ANY; + *lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE; + *lpResourceUsage = RESOURCEUSAGE_CONTAINER; + + // + // Need to build a string with the new NDS UNC path for subtree object + // + treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( TreeServerName.Length + 3 ) * sizeof(WCHAR) ); + + if ( treeNameStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto ErrorExit; + } + + wcscpy( treeNameStr, L"\\\\" ); + wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length ); + _wcsupr( treeNameStr ); + + *lpFullNDSObjectNamePath = treeNameStr; + + return NO_ERROR; + } + else + { + WCHAR lpServerName[NW_MAX_SERVER_LEN]; + UNICODE_STRING ServerName; + + ServerName.Length = 0; + ServerName.MaximumLength = sizeof( lpServerName ); + ServerName.Buffer = lpServerName; + + // + // Resolve the path to get a NDS object id. + // + ntstatus = NwNdsResolveName( ConnectionHandle, + &PathString, + &dwOid, + &ServerName, + NULL, + 0 ); + + if ( ntstatus == STATUS_SUCCESS && ServerName.Length ) + { + DWORD dwHandleType; + + // + // NwNdsResolveName succeeded, but we were referred to + // another server, though ContextHandle->dwOid is still valid. + + if ( ConnectionHandle ) + CloseHandle( ConnectionHandle ); + + ConnectionHandle = NULL; + + // + // Open a NDS generic connection handle to \\ServerName + // + ntstatus = NwNdsOpenGenericHandle( &ServerName, + &dwHandleType, + &ConnectionHandle ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + ASSERT( dwHandleType != HANDLE_TYPE_NCP_SERVER ); + } + } + + if ( ntstatus != STATUS_SUCCESS ) + { + LPWSTR treeNameStr = NULL; + + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = RESOURCETYPE_ANY; + *lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE; + *lpResourceUsage = RESOURCEUSAGE_CONTAINER; + + // + // Need to build a string with the new NDS UNC path for subtree object + // + treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( TreeServerName.Length + 3 ) * sizeof(WCHAR) ); + + if ( treeNameStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto ErrorExit; + } + + wcscpy( treeNameStr, L"\\\\" ); + wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length ); + _wcsupr( treeNameStr ); + + *lpFullNDSObjectNamePath = treeNameStr; + + status = VERIFY_ERROR_PATH_NOT_FOUND; + goto ErrorExit; + } + + // + // Check to see what kind of object is pointed to by lpRemoteName. + // + { + BYTE RawResponse[TWO_KB]; + PBYTE pbRawGetInfo; + DWORD RawResponseSize = sizeof(RawResponse); + DWORD dwStrLen; + DWORD TreeObjectName; + LPWSTR StrippedObjectName = NULL; + LPWSTR newPathStr = NULL; + + ntstatus = NwNdsReadObjectInfo( ConnectionHandle, + dwOid, + RawResponse, + RawResponseSize ); + + if ( ntstatus != NO_ERROR ) + { + status = RtlNtStatusToDosError(ntstatus); + goto ErrorExit; + } + + // + // Get current subtree data from ContextHandle + // + *lpClassType = NwGetSubTreeData( (DWORD) RawResponse, + &TreeObjectName, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage, + &StrippedObjectName ); + + if ( StrippedObjectName == NULL ) + { + KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto ErrorExit; + } + + // + // Need to build a string with the new NDS UNC path for subtree object + // + newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( StrippedObjectName ) + + TreeServerName.Length + 4 ) + * sizeof(WCHAR) ); + + if ( newPathStr == NULL ) + { + (void) LocalFree((HLOCAL) StrippedObjectName); + + KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto ErrorExit; + } + + wcscpy( newPathStr, L"\\\\" ); + wcsncat( newPathStr, TreeServerName.Buffer, TreeServerName.Length ); + wcscat( newPathStr, L"\\" ); + wcscat( newPathStr, StrippedObjectName ); + _wcsupr( newPathStr ); + + // + // Don't need the StrippedObjectName string anymore + // + (void) LocalFree((HLOCAL) StrippedObjectName); + StrippedObjectName = NULL; + + *lpFullNDSObjectNamePath = newPathStr; + status = NO_ERROR; + } // End of Block + +ErrorExit: + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( ConnectionHandle ) + CloseHandle( ConnectionHandle ); + + return status; +} + + +DWORD +NwVerifyBinderyObject( + IN LPWSTR lpBinderyObjectPathName, + OUT LPWSTR * lpFullBinderyObjectPathName, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ) +{ + DWORD status = NO_ERROR; + HANDLE ConnectionHandle = NULL; + BOOL fImpersonate = FALSE ; + BOOL fResourceTypeDisk = FALSE ; + BOOL fIsNdsUnc = FALSE ; + UNICODE_STRING BinderyConnectStr; + ULONG CreateDisposition = 0; + ULONG CreateOptions = 0; + WORD wSlashCount; + LPWSTR FourthSlash; + + if ( lpBinderyObjectPathName == NULL ) + { + // + // Handle this as if we are at the root of our provider hierarchy. + // + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = RESOURCETYPE_ANY; +#ifdef NT1057 + *lpResourceDisplayType = 0; +#else + *lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK; +#endif + *lpResourceUsage = RESOURCEUSAGE_CONTAINER; + + *lpFullBinderyObjectPathName = NULL; + + return NO_ERROR; + } + + // + // Find out if we are looking at a \\server, \\server\vol, or + // \\server\vol\dir . . . + // + NwpGetUncInfo( lpBinderyObjectPathName, + &wSlashCount, + &fIsNdsUnc, + &FourthSlash ); + + if ( wSlashCount > 2 ) + fResourceTypeDisk = TRUE; + + // + // Impersonate the client + // + if ( ( status = NwImpersonateClient() ) != NO_ERROR ) + { + goto ErrorExit; + } + + fImpersonate = TRUE; + + // + // Open a connection handle to \\server\vol\... + // + + BinderyConnectStr.Buffer = NULL; + + // + // Open a tree connection handle to \Device\NwRdr\ContainerName + // + status = NwCreateTreeConnectName( lpBinderyObjectPathName, + NULL, + &BinderyConnectStr ); + + if ( status != NO_ERROR ) + { + status = WN_BAD_NETNAME; + goto ErrorExit; + } + + CreateDisposition = FILE_OPEN; + CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT; + + status = NwOpenCreateConnection( &BinderyConnectStr, + NULL, + NULL, + lpBinderyObjectPathName, + FILE_LIST_DIRECTORY | SYNCHRONIZE, + CreateDisposition, + CreateOptions, + RESOURCETYPE_DISK, // When connecting beyond servername + &ConnectionHandle, + NULL ); + + if ( status == NO_ERROR ) + { + LPWSTR BinderyNameStr = NULL; + + // + // Need to build a string with the new UNC path for bindery object + // + BinderyNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( lpBinderyObjectPathName ) + 1 ) + * sizeof(WCHAR) ); + + if ( BinderyNameStr == NULL ) + { + KdPrint(("NWWORKSTATION: NwVerifyBinderyObject LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto ErrorExit; + } + + wcscpy( BinderyNameStr, lpBinderyObjectPathName ); + _wcsupr( BinderyNameStr ); + + *lpFullBinderyObjectPathName = BinderyNameStr; + + if ( BinderyConnectStr.Buffer ) + (void) LocalFree((HLOCAL) BinderyConnectStr.Buffer); + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( ConnectionHandle ) + { + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = fResourceTypeDisk ? RESOURCETYPE_DISK : RESOURCETYPE_ANY; + *lpResourceDisplayType = fResourceTypeDisk ? RESOURCEDISPLAYTYPE_SHARE : RESOURCEDISPLAYTYPE_SERVER; +#ifdef NT1057 + *lpResourceUsage = fResourceTypeDisk ? RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER : RESOURCEUSAGE_CONTAINER; +#else + *lpResourceUsage = fResourceTypeDisk ? RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE : RESOURCEUSAGE_CONTAINER; +#endif + + CloseHandle( ConnectionHandle ); + } + + return NO_ERROR; + } + +ErrorExit: + + *lpFullBinderyObjectPathName = NULL; + + if ( BinderyConnectStr.Buffer ) + (void) LocalFree((HLOCAL) BinderyConnectStr.Buffer); + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( ConnectionHandle ) + CloseHandle( ConnectionHandle ); + + return WN_BAD_NETNAME; +} + + +DWORD +NwGetNDSPathInfo( + IN LPWSTR lpNDSObjectNamePath, + OUT LPWSTR * lppSystemObjectNamePath, + OUT LPWSTR * lpSystemPathPart, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ) +{ + DWORD status = NO_ERROR; + WORD slashCount; + BOOL isNdsUnc; + BOOL fReturnBadNetName = FALSE; + LPWSTR FourthSlash; + LPWSTR lpSystemPath = NULL; + + *lpSystemPathPart = NULL; + + NwpGetUncInfo( lpNDSObjectNamePath, + &slashCount, + &isNdsUnc, + &FourthSlash ); + + if ( slashCount <= 3 ) + { + // + // Path is to a possible NDS object, check to see if so and if valid... + // + + status = NwVerifyNDSObject( lpNDSObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + + *lpSystemPathPart = NULL; + + return status; + } + else + { + // + // Path is to a directory, see if directory exists . . . + // + status = NwVerifyBinderyObject( lpNDSObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + } + + if ( status == WN_BAD_NETNAME ) + { + fReturnBadNetName = TRUE; + status = NO_ERROR; + } + + if ( status == NO_ERROR ) + { + WCHAR TempNDSObjectNamePath[256]; + + // + // Test \\tree\obj.obj... component and + // return network resource for valid parent and the string, + // lpSystemPathPart, for the directory part ( \dir1\...). + // + + if ( *lppSystemObjectNamePath != NULL ) + { + (void) LocalFree( (HLOCAL) (*lppSystemObjectNamePath) ); + *lppSystemObjectNamePath = NULL; + } + + lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( FourthSlash ) + 1 ) * + sizeof( WCHAR ) ); + + if ( lpSystemPath == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy( lpSystemPath, FourthSlash ); + *FourthSlash = L'\0'; + + wcscpy( TempNDSObjectNamePath, lpNDSObjectNamePath ); + *FourthSlash = L'\\'; + + // + // See if \\tree\obj.obj.... exists . . . + // + status = NwVerifyNDSObject( TempNDSObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + + if ( status != NO_ERROR ) + { + LocalFree( lpSystemPath ); + lpSystemPath = NULL; + } + } + + *lpSystemPathPart = lpSystemPath; + + // + // The provider spec for this function used to tell us to create a + // NETRESOURCE, even if the system part of the path was invalid, while + // returning WN_BAD_NETNAME. Now we return SUCCESS and the NETRESOURCE, + // irregardless of whether the lpSystem part is valid. + // if ( fReturnBadNetName == TRUE ) + // { + // return WN_BAD_NETNAME; + // } + + return status; +} + + +DWORD +NwGetBinderyPathInfo( + IN LPWSTR lpBinderyObjectNamePath, + OUT LPWSTR * lppSystemObjectNamePath, + OUT LPWSTR * lpSystemPathPart, + OUT LPDWORD lpClassType, + OUT LPDWORD lpResourceScope, + OUT LPDWORD lpResourceType, + OUT LPDWORD lpResourceDisplayType, + OUT LPDWORD lpResourceUsage + ) +{ + DWORD status = NO_ERROR; + WORD slashCount; + BOOL isNdsUnc; + LPWSTR FourthSlash; + LPWSTR lpSystemPath = NULL; + + *lpSystemPathPart = NULL; + + NwpGetUncInfo( lpBinderyObjectNamePath, + &slashCount, + &isNdsUnc, + &FourthSlash ); + + if ( slashCount <= 3 ) + { + // + // Path is to a server or volume, check to see which and if valid . . . + // + + status = NwVerifyBinderyObject( lpBinderyObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + + *lpSystemPathPart = NULL; + + return status; + } + else + { + // + // Path is to a directory, see if directory exists . . . + // + status = NwVerifyBinderyObject( lpBinderyObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + } + + if ( status == WN_BAD_NETNAME ) + { + WCHAR TempBinderyObjectNamePath[256]; + + // + // Path is to a invalid directory. Test \\server\volume component and + // return network resource for valid parent and the string, + // lpSystemPathPart, for the directory part ( \dir1\...). + // + + lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( FourthSlash ) + 1 ) * + sizeof( WCHAR ) ); + + if ( lpSystemPath == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy( lpSystemPath, FourthSlash ); + *FourthSlash = L'\0'; + + wcscpy( TempBinderyObjectNamePath, lpBinderyObjectNamePath ); + *FourthSlash = L'\\'; + + // + // See if \\server\volume exists . . . + // + status = NwVerifyBinderyObject( TempBinderyObjectNamePath, + lppSystemObjectNamePath, + lpClassType, + lpResourceScope, + lpResourceType, + lpResourceDisplayType, + lpResourceUsage ); + + if ( status != NO_ERROR ) + { + LocalFree( lpSystemPath ); + lpSystemPath = NULL; + } + + // + // Return SUCCESS, since the NETRESOURCE for \\server\volume that + // we are describing is at least valid, even though the lpSystem + // part in not. This is a change in the provider spec (4/25/96). + // + // else + // { + // status = WN_BAD_NETNAME; + // } + } + else + { + // + // Path is to a valid directory. Return resource information for the + // \\server\volume component and the string, lpSystemPathPart, for the + // directory part ( \dir1\...). + // + NwpGetUncInfo( *lppSystemObjectNamePath, + &slashCount, + &isNdsUnc, + &FourthSlash ); + + lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, + ( wcslen( FourthSlash ) + 1 ) * + sizeof( WCHAR ) ); + + if ( lpSystemPath == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy( lpSystemPath, FourthSlash ); + *FourthSlash = L'\0'; + + *lpResourceScope = RESOURCE_GLOBALNET; + *lpResourceType = RESOURCETYPE_DISK; + *lpResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE; +#ifdef NT1057 + *lpResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER; +#else + *lpResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE; +#endif + + status = NO_ERROR; + } + + *lpSystemPathPart = lpSystemPath; + + return status; +} + + +BOOL +NwGetRemoteNameParent( + IN LPWSTR lpRemoteName, + OUT LPWSTR * lpRemoteNameParent + ) +{ + unsigned short iter = 0; + unsigned short totalLength = wcslen( lpRemoteName ); + unsigned short slashCount = 0; + unsigned short dotCount = 0; + unsigned short thirdSlash = 0; + unsigned short lastSlash = 0; + unsigned short parentNDSSubTree = 0; + LPWSTR newRemoteNameParent = NULL; + + if ( totalLength < 2 ) + return FALSE; + + // + // Get thirdSlash to indicate the character in the string that indicates the + // "\" in between the tree name and the rest of the UNC path. Set parentNDSSubTree + // if available. And always set lastSlash to the most recent "\" seen as you walk. + // + // Example: \\<tree name>\path.to.object[\|.]<object> + // ^ ^ + // | | + // thirdSlash parentNDSSubTree + // + while ( iter < totalLength ) + { + if ( lpRemoteName[iter] == L'\\' ) + { + slashCount += 1; + if ( slashCount == 3 ) + thirdSlash = iter; + + lastSlash = iter; + } + + if ( lpRemoteName[iter] == L'.' ) + { + dotCount += 1; + if ( dotCount == 1 ) + parentNDSSubTree = iter; + } + + iter++; + } + + if ( slashCount > 3 ) + { + newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( lastSlash + 1 ) * + sizeof(WCHAR)); + + if ( newRemoteNameParent == NULL ) + { + KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n", + GetLastError())); + return FALSE; + } + + wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash ); + _wcsupr( newRemoteNameParent ); + + *lpRemoteNameParent = newRemoteNameParent; + + return TRUE; + } + + if ( slashCount == 3 ) + { + if ( dotCount == 0 ) + { + newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( lastSlash + 1 ) * + sizeof(WCHAR)); + + if ( newRemoteNameParent == NULL ) + { + KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n", + GetLastError())); + return FALSE; + } + + wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash ); + _wcsupr( newRemoteNameParent ); + + *lpRemoteNameParent = newRemoteNameParent; + + return TRUE; + } + else + { + newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT, + ( totalLength - + ( parentNDSSubTree - thirdSlash ) + + 1 ) + * sizeof(WCHAR) ); + + if ( newRemoteNameParent == NULL ) + { + KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n", + GetLastError())); + return FALSE; + } + + wcsncpy( newRemoteNameParent, lpRemoteName, thirdSlash + 1 ); + wcscat( newRemoteNameParent, &lpRemoteName[parentNDSSubTree+1] ); + _wcsupr( newRemoteNameParent ); + + *lpRemoteNameParent = newRemoteNameParent; + + return TRUE; + } + } + + // Else we set lpRemoteNameParent to NULL, to indicate that we are at the top and + // return TRUE. + *lpRemoteNameParent = NULL; + + return TRUE; +} + + +DWORD +NwGetFirstDirectoryEntry( + IN HANDLE DirHandle, + OUT LPWSTR *DirEntry + ) +/*++ + +Routine Description: + + This function is called by NwEnumDirectories to get the first + directory entry given a handle to the directory. It allocates + the output buffer to hold the returned directory name; the + caller should free this output buffer with LocalFree when done. + +Arguments: + + DirHandle - Supplies the opened handle to the container + directory find a directory within it. + + DirEntry - Receives a pointer to the returned directory + found. + +Return Value: + + NO_ERROR - The operation was successful. + + ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output + buffer. + + Other errors from NtQueryDirectoryFile. + +--*/ // NwGetFirstDirectoryEntry +{ + DWORD status = NO_ERROR; + NTSTATUS ntstatus = STATUS_SUCCESS; + IO_STATUS_BLOCK IoStatusBlock; + + PFILE_DIRECTORY_INFORMATION DirInfo; + + UNICODE_STRING StartFileName; + +#if DBG + DWORD i = 0; +#endif + + // + // Allocate a large buffer to get one directory information entry. + // + DirInfo = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + sizeof(FILE_DIRECTORY_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)) + ); + + if (DirInfo == NULL) { + KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + RtlInitUnicodeString(&StartFileName, L"*"); + + ntstatus = NtQueryDirectoryFile( + DirHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + DirInfo, + sizeof(FILE_DIRECTORY_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)), + FileDirectoryInformation, // Info class requested + TRUE, // Return single entry + &StartFileName, // Redirector needs this + TRUE // Restart scan + ); + + // + // For now, if buffer to NtQueryDirectoryFile is too small, just give + // up. We may want to try to reallocate a bigger buffer at a later time. + // + + if (ntstatus == STATUS_SUCCESS) { + ntstatus = IoStatusBlock.Status; + } + + if (ntstatus != STATUS_SUCCESS) { + + if (ntstatus == STATUS_NO_MORE_FILES) { + // + // We ran out of entries. + // + status = WN_NO_MORE_ENTRIES; + } + else { + KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n", + ntstatus)); + status = RtlNtStatusToDosError(ntstatus); + } + + goto CleanExit; + } + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i, + DirInfo->FileName, DirInfo->FileAttributes)); + } +#endif + + // + // Scan until we find the first directory entry that is not "." or ".." + // + while (!(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + memcmp(DirInfo->FileName, L".", DirInfo->FileNameLength) == 0 || + memcmp(DirInfo->FileName, L"..", DirInfo->FileNameLength) == 0) { + + ntstatus = NtQueryDirectoryFile( + DirHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + DirInfo, + sizeof(FILE_DIRECTORY_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)), + FileDirectoryInformation, // Info class requested + TRUE, // Return single entry + NULL, + FALSE // Restart scan + ); + + if (ntstatus == STATUS_SUCCESS) { + ntstatus = IoStatusBlock.Status; + } + + if (ntstatus != STATUS_SUCCESS) { + + if (ntstatus == STATUS_NO_MORE_FILES) { + // + // We ran out of entries. + // + status = WN_NO_MORE_ENTRIES; + } + else { + KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n", + ntstatus)); + status = RtlNtStatusToDosError(ntstatus); + } + + goto CleanExit; + } + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i, + DirInfo->FileName, DirInfo->FileAttributes)); + } +#endif + } + + // + // Allocate the output buffer for the returned directory name + // + *DirEntry = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + DirInfo->FileNameLength + sizeof(WCHAR) + ); + + if (*DirEntry == NULL) { + KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto CleanExit; + } + + memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry returns %ws\n", + *DirEntry)); + } +#endif + + status = NO_ERROR; + +CleanExit: + (void) LocalFree((HLOCAL) DirInfo); + + // + // We could not find any directories under the requested + // so we need to treat this as no entries. + // + if ( status == ERROR_FILE_NOT_FOUND ) + status = WN_NO_MORE_ENTRIES; + + return status; +} + + + +DWORD +NwGetNextDirectoryEntry( + IN HANDLE DirHandle, + OUT LPWSTR *DirEntry + ) +/*++ + +Routine Description: + + This function is called by NwEnumDirectories to get the next + directory entry given a handle to the directory. It allocates + the output buffer to hold the returned directory name; the + caller should free this output buffer with LocalFree when done. + +Arguments: + + DirHandle - Supplies the opened handle to the container + directory find a directory within it. + + DirEntry - Receives a pointer to the returned directory + found. + +Return Value: + + NO_ERROR - The operation was successful. + + ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output + buffer. + + Other errors from NtQueryDirectoryFile. + +--*/ // NwGetNextDirectoryEntry +{ + DWORD status = NO_ERROR; + NTSTATUS ntstatus = STATUS_SUCCESS; + IO_STATUS_BLOCK IoStatusBlock; + + PFILE_DIRECTORY_INFORMATION DirInfo; + + // + // Allocate a large buffer to get one directory information entry. + // + DirInfo = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + sizeof(FILE_DIRECTORY_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)) + ); + + if (DirInfo == NULL) { + KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + do { + + ntstatus = NtQueryDirectoryFile( + DirHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + DirInfo, + sizeof(FILE_DIRECTORY_INFORMATION) + + (MAX_PATH * sizeof(WCHAR)), + FileDirectoryInformation, // Info class requested + TRUE, // Return single entry + NULL, + FALSE // Restart scan + ); + + if (ntstatus == STATUS_SUCCESS) { + ntstatus = IoStatusBlock.Status; + } + + } while (ntstatus == STATUS_SUCCESS && + !(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + + + if (ntstatus != STATUS_SUCCESS) { + + if (ntstatus == STATUS_NO_MORE_FILES) { + // + // We ran out of entries. + // + status = WN_NO_MORE_ENTRIES; + } + else { + KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry: NtQueryDirectoryFile returns %08lx\n", + ntstatus)); + status = RtlNtStatusToDosError(ntstatus); + } + + goto CleanExit; + } + + + // + // Allocate the output buffer for the returned directory name + // + *DirEntry = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + DirInfo->FileNameLength + sizeof(WCHAR) + ); + + if (*DirEntry == NULL) { + KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n", + GetLastError())); + status = ERROR_NOT_ENOUGH_MEMORY; + goto CleanExit; + } + + memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength); + +#if DBG + IF_DEBUG(ENUM) { + KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry returns %ws\n", + *DirEntry)); + } +#endif + + status = NO_ERROR; + +CleanExit: + (void) LocalFree((HLOCAL) DirInfo); + + return status; +} + + +DWORD +NwWriteNetResourceEntry( + IN OUT LPBYTE * FixedPortion, + IN OUT LPWSTR * EndOfVariableData, + IN LPWSTR ContainerName OPTIONAL, + IN LPWSTR LocalName OPTIONAL, + IN LPWSTR RemoteName, + IN DWORD ScopeFlag, + IN DWORD DisplayFlag, + IN DWORD UsageFlag, + IN DWORD ResourceType, + IN LPWSTR SystemPath OPTIONAL, + OUT LPWSTR * lppSystem OPTIONAL, + OUT LPDWORD EntrySize + ) +/*++ + +Routine Description: + + This function packages a NETRESOURCE entry into the user output buffer. + It is called by the various enum resource routines. + +Arguments: + + FixedPortion - Supplies a pointer to the output buffer where the next + entry of the fixed portion of the use information will be written. + This pointer is updated to point to the next fixed portion entry + after a NETRESOURCE entry is written. + + EndOfVariableData - Supplies a pointer just off the last available byte + in the output buffer. This is because the variable portion of the + user information is written into the output buffer starting from + the end. + + This pointer is updated after any variable length information is + written to the output buffer. + + ContainerName - Supplies the full path qualifier to make RemoteName + a full UNC name. + + LocalName - Supplies the local device name, if any. + + RemoteName - Supplies the remote resource name. + + ScopeFlag - Supplies the flag which indicates whether this is a + CONNECTED or GLOBALNET resource. + + DisplayFlag - Supplies the flag which tells the UI how to display + the resource. + + UsageFlag - Supplies the flag which indicates that the RemoteName + is either a container or a connectable resource or both. + + SystemPath - Supplies the optional system path data to be stored in the + NETRESOURCE buffer. This is used by the NPGetResourceInformation + helper routines. + + lppSystem - If SystemPath is provided, this will point to the location + in the NETRESOURCE buffer that contains the system path string. + + EntrySize - Receives the size of the NETRESOURCE entry in bytes. + +Return Value: + + NO_ERROR - Successfully wrote entry into user buffer. + + ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer. + + WN_MORE_DATA - Buffer was too small to fit entry. + +--*/ // NwWriteNetResourceEntry +{ + BOOL FitInBuffer = TRUE; + LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion; + LPWSTR RemoteBuffer; + LPWSTR lpSystem; + + *EntrySize = sizeof(NETRESOURCEW) + + (wcslen(RemoteName) + wcslen(NwProviderName) + 2) * + sizeof(WCHAR); + + + if (ARGUMENT_PRESENT(LocalName)) { + *EntrySize += (wcslen(LocalName) + 1) * sizeof(WCHAR); + } + + if (ARGUMENT_PRESENT(ContainerName)) { + *EntrySize += wcslen(ContainerName) * sizeof(WCHAR); + } + + if (ARGUMENT_PRESENT(SystemPath)) { + *EntrySize += wcslen(SystemPath) * sizeof(WCHAR); + } + + *EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD); + + // + // See if buffer is large enough to fit the entry. + // + if (((DWORD) *FixedPortion + *EntrySize) > + (DWORD) *EndOfVariableData) { + + return WN_MORE_DATA; + } + + NetR->dwScope = ScopeFlag; + NetR->dwType = ResourceType; + NetR->dwDisplayType = DisplayFlag; + NetR->dwUsage = UsageFlag; + NetR->lpComment = NULL; + + // + // Update fixed entry pointer to next entry. + // + (DWORD) (*FixedPortion) += sizeof(NETRESOURCEW); + + // + // RemoteName + // + if (ARGUMENT_PRESENT(ContainerName)) { + + // + // Prefix the RemoteName with its container name making the + // it a fully-qualified UNC name. + // + RemoteBuffer = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + (wcslen(RemoteName) + wcslen(ContainerName) + 1) * + sizeof(WCHAR) + ); + + if (RemoteBuffer == NULL) { + KdPrint(("NWWORKSTATION: NwWriteNetResourceEntry LocalAlloc failed %lu\n", + GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy(RemoteBuffer, ContainerName); + wcscat(RemoteBuffer, RemoteName); + } + else { + RemoteBuffer = RemoteName; + } + + FitInBuffer = NwlibCopyStringToBuffer( + RemoteBuffer, + wcslen(RemoteBuffer), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpRemoteName + ); + + if (ARGUMENT_PRESENT(ContainerName)) { + (void) LocalFree((HLOCAL) RemoteBuffer); + } + + ASSERT(FitInBuffer); + + // + // LocalName + // + if (ARGUMENT_PRESENT(LocalName)) { + FitInBuffer = NwlibCopyStringToBuffer( + LocalName, + wcslen(LocalName), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpLocalName + ); + + ASSERT(FitInBuffer); + } + else { + NetR->lpLocalName = NULL; + } + + // + // SystemPath + // + if (ARGUMENT_PRESENT(SystemPath)) { + FitInBuffer = NwlibCopyStringToBuffer( + SystemPath, + wcslen(SystemPath), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &lpSystem + ); + + ASSERT(FitInBuffer); + } + else { + lpSystem = NULL; + } + + if (ARGUMENT_PRESENT(lppSystem)) { + *lppSystem = lpSystem; + } + + // + // ProviderName + // + FitInBuffer = NwlibCopyStringToBuffer( + NwProviderName, + wcslen(NwProviderName), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpProvider + ); + + ASSERT(FitInBuffer); + + if (! FitInBuffer) { + return WN_MORE_DATA; + } + + return NO_ERROR; +} + + +DWORD +NwWritePrinterInfoEntry( + IN OUT LPBYTE *FixedPortion, + IN OUT LPWSTR *EndOfVariableData, + IN LPWSTR ContainerName OPTIONAL, + IN LPWSTR RemoteName, + IN DWORD Flags, + OUT LPDWORD EntrySize + ) +/*++ + +Routine Description: + + This function packages a PRINTER_INFO_1 entry into the user output buffer. + +Arguments: + + FixedPortion - Supplies a pointer to the output buffer where the next + entry of the fixed portion of the use information will be written. + This pointer is updated to point to the next fixed portion entry + after a PRINT_INFO_1 entry is written. + + EndOfVariableData - Supplies a pointer just off the last available byte + in the output buffer. This is because the variable portion of the + user information is written into the output buffer starting from + the end. + + This pointer is updated after any variable length information is + written to the output buffer. + + ContainerName - Supplies the full path qualifier to make RemoteName + a full UNC name. + + RemoteName - Supplies the remote resource name. + + Flags - Supplies the flag which indicates that the RemoteName + is either a container or not and the icon to use. + + EntrySize - Receives the size of the PRINTER_INFO_1 entry in bytes. + +Return Value: + + NO_ERROR - Successfully wrote entry into user buffer. + + ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer. + + ERROR_INSUFFICIENT_BUFFER - Buffer was too small to fit entry. + +--*/ // NwWritePrinterInfoEntry +{ + BOOL FitInBuffer = TRUE; + PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) *FixedPortion; + LPWSTR RemoteBuffer; + + *EntrySize = sizeof(PRINTER_INFO_1W) + + ( 2 * wcslen(RemoteName) + 2) * sizeof(WCHAR); + + if (ARGUMENT_PRESENT(ContainerName)) { + *EntrySize += wcslen(ContainerName) * sizeof(WCHAR); + } + else { + // 3 is for the length of "!\\" + *EntrySize += (wcslen(NwProviderName) + 3) * sizeof(WCHAR); + } + + *EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD); + + // + // See if buffer is large enough to fit the entry. + // + if (((DWORD) *FixedPortion + *EntrySize) > + (DWORD) *EndOfVariableData) { + + return ERROR_INSUFFICIENT_BUFFER; + } + + pPrinterInfo1->Flags = Flags; + pPrinterInfo1->pComment = NULL; + + // + // Update fixed entry pointer to next entry. + // + (DWORD) (*FixedPortion) += sizeof(PRINTER_INFO_1W); + + // + // Name + // + if (ARGUMENT_PRESENT(ContainerName)) { + + // + // Prefix the RemoteName with its container name making the + // it a fully-qualified UNC name. + // + RemoteBuffer = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + (wcslen(ContainerName) + wcslen(RemoteName) + + 1) * sizeof(WCHAR) ); + + if (RemoteBuffer == NULL) { + KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy(RemoteBuffer, ContainerName); + wcscat(RemoteBuffer, RemoteName); + } + else { + // + // Prefix the RemoteName with its provider name + // + RemoteBuffer = (PVOID) LocalAlloc( + LMEM_ZEROINIT, + (wcslen(RemoteName) + + wcslen(NwProviderName) + 4) + * sizeof(WCHAR) ); + + if (RemoteBuffer == NULL) { + KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError())); + return ERROR_NOT_ENOUGH_MEMORY; + } + + wcscpy(RemoteBuffer, NwProviderName ); + wcscat(RemoteBuffer, L"!\\\\" ); + wcscat(RemoteBuffer, RemoteName); + } + + FitInBuffer = NwlibCopyStringToBuffer( + RemoteBuffer, + wcslen(RemoteBuffer), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &pPrinterInfo1->pName ); + + (void) LocalFree((HLOCAL) RemoteBuffer); + + ASSERT(FitInBuffer); + + // + // Description + // + FitInBuffer = NwlibCopyStringToBuffer( + RemoteName, + wcslen(RemoteName), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &pPrinterInfo1->pDescription ); + + ASSERT(FitInBuffer); + + if (! FitInBuffer) { + return ERROR_INSUFFICIENT_BUFFER; + } + + return NO_ERROR; +} + + +int _CRTAPI1 +SortFunc( + IN CONST VOID *p1, + IN CONST VOID *p2 +) +/*++ + +Routine Description: + + This function is used in qsort to compare the descriptions of + two printer_info_1 structure. + +Arguments: + + p1 - Points to a PRINTER_INFO_1 structure + p2 - Points to a PRINTER_INFO_1 structure to compare with p1 + +Return Value: + + Same as return value of lstrccmpi. + +--*/ +{ + PRINTER_INFO_1W *pFirst = (PRINTER_INFO_1W *) p1; + PRINTER_INFO_1W *pSecond = (PRINTER_INFO_1W *) p2; + + return lstrcmpiW( pFirst->pDescription, pSecond->pDescription ); +} + + + +DWORD +NwGetConnectionInformation( + IN LPWSTR lpName, + OUT LPWSTR lpUserName, + OUT LPWSTR lpHostServer + ) +{ + DWORD status = NO_ERROR; + NTSTATUS ntstatus = STATUS_SUCCESS; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY; + HANDLE hRdr = NULL; + BOOL fImpersonate = FALSE ; + + WCHAR OpenString[] = L"\\Device\\Nwrdr\\*"; + UNICODE_STRING OpenName; + + OEM_STRING OemArg; + UNICODE_STRING ConnectionName; + WCHAR ConnectionBuffer[512]; + + ULONG BufferSize = 512; + ULONG RequestSize, ReplyLen; + PNWR_REQUEST_PACKET Request; + BYTE *Reply; + + PCONN_INFORMATION pConnInfo; + UNICODE_STRING Name; + + // + // Allocate buffer space. + // + + Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize ); + + if ( !Request ) + { + status = ERROR_NOT_ENOUGH_MEMORY; + + goto ErrorExit; + } + + // + // Impersonate the client + // + if ( ( status = NwImpersonateClient() ) != NO_ERROR ) + { + goto ErrorExit; + } + + fImpersonate = TRUE; + + // + // Convert the connect name to unicode. + // + ConnectionName.Length = wcslen( lpName )* sizeof(WCHAR); + ConnectionName.MaximumLength = sizeof( ConnectionBuffer ); + ConnectionName.Buffer = ConnectionBuffer; + + wcscpy( ConnectionName.Buffer, lpName ); + _wcsupr( ConnectionName.Buffer ); + + // + // Set up the object attributes. + // + + RtlInitUnicodeString( &OpenName, OpenString ); + + InitializeObjectAttributes( &ObjectAttributes, + &OpenName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL ); + + ntstatus = NtOpenFile( &hRdr, + DesiredAccess, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_VALID_FLAGS, + FILE_SYNCHRONOUS_IO_NONALERT ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + + goto ErrorExit; + } + + // + // Fill out the request packet for FSCTL_NWR_GET_CONN_INFO. + // + + Request->Parameters.GetConnInfo.ConnectionNameLength = ConnectionName.Length; + RtlCopyMemory( &(Request->Parameters.GetConnInfo.ConnectionName[0]), + ConnectionBuffer, + ConnectionName.Length ); + + RequestSize = sizeof( Request->Parameters.GetConnInfo ) + ConnectionName.Length; + Reply = ((PBYTE)Request) + RequestSize; + ReplyLen = BufferSize - RequestSize; + + ntstatus = NtFsControlFile( hRdr, + NULL, + NULL, + NULL, + &IoStatusBlock, + FSCTL_NWR_GET_CONN_INFO, + (PVOID) Request, + RequestSize, + (PVOID) Reply, + ReplyLen ); + + if ( ntstatus != STATUS_SUCCESS ) + { + status = RtlNtStatusToDosError(ntstatus); + + goto ErrorExit; + } + + (void) NwRevertToSelf() ; + fImpersonate = FALSE; + + NtClose( hRdr ); + + pConnInfo = (PCONN_INFORMATION) Reply; + wcscpy( lpUserName, pConnInfo->UserName ); + wcscpy( lpHostServer, pConnInfo->HostServer ); + + LocalFree( Request ); + + return NO_ERROR; + +ErrorExit: + + if ( fImpersonate ) + (void) NwRevertToSelf() ; + + if ( Request ) + LocalFree( Request ); + + if ( hRdr ) + NtClose( hRdr ); + + return status; +} + + +VOID +NwpGetUncInfo( + IN LPWSTR lpstrUnc, + OUT WORD * slashCount, + OUT BOOL * isNdsUnc, + OUT LPWSTR * FourthSlash + ) +{ + BYTE i; + WORD length = wcslen( lpstrUnc ); + + *isNdsUnc = FALSE; + *slashCount = 0; + *FourthSlash = NULL; + + for ( i = 0; i < length; i++ ) + { + if ( lpstrUnc[i] == L'=' ) + { + *isNdsUnc = TRUE; + } + + if ( lpstrUnc[i] == L'\\' ) + { + *slashCount += 1; + + if ( *slashCount == 4 ) + { + *FourthSlash = &lpstrUnc[i]; + } + } + } +} + + +DWORD +NwpGetCurrentUserRegKey( + IN DWORD DesiredAccess, + OUT HKEY *phKeyCurrentUser + ) +/*++ + +Routine Description: + + This routine opens the current user's registry key under + \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWCWorkstation\Parameters + +Arguments: + + DesiredAccess - The access mask to open the key with + + phKeyCurrentUser - Receives the opened key handle + +Return Value: + + Returns the appropriate Win32 error. + +--*/ +{ + DWORD err; + HKEY hkeyWksta; + LPWSTR CurrentUser; + + // + // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services + // \NWCWorkstation\Parameters + // + err = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + NW_WORKSTATION_REGKEY, + REG_OPTION_NON_VOLATILE, + KEY_READ, + &hkeyWksta + ); + + if ( err ) { + KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters key unexpected error %lu!\n", err)); + return err; + } + // + // Get the current user's SID string. + // + err = NwReadRegValue( + hkeyWksta, + NW_CURRENTUSER_VALUENAME, + &CurrentUser + ); + + + if ( err ) { + KdPrint(("NWPROVAU: NwGetCurrentUserRegKey read CurrentUser value unexpected error %lu!\n", err)); + (void) RegCloseKey( hkeyWksta ); + return err; + } + + (void) RegCloseKey( hkeyWksta ); + + // + // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services + // \NWCWorkstation\Parameters\Option + // + err = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + NW_WORKSTATION_OPTION_REGKEY, + REG_OPTION_NON_VOLATILE, + KEY_READ, + &hkeyWksta + ); + + if ( err ) { + KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters\\Option key unexpected error %lu!\n", err)); + return err; + } + + // + // Open current user's key + // + err = RegOpenKeyExW( + hkeyWksta, + CurrentUser, + REG_OPTION_NON_VOLATILE, + DesiredAccess, + phKeyCurrentUser + ); + + if ( err == ERROR_FILE_NOT_FOUND) + { + DWORD Disposition; + + // + // Create <NewUser> key under NWCWorkstation\Parameters\Option + // + err = RegCreateKeyExW( + hkeyWksta, + CurrentUser, + 0, + WIN31_CLASS, + REG_OPTION_NON_VOLATILE, + DesiredAccess, + NULL, // security attr + phKeyCurrentUser, + &Disposition + ); + + } + + if ( err ) { + KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open or create of Parameters\\Option\\%ws key failed %lu\n", CurrentUser, err)); + } + + (void) RegCloseKey( hkeyWksta ); + (void) LocalFree((HLOCAL)CurrentUser) ; + return err; +} + + +DWORD +NwQueryInfo( + OUT LPWSTR *ppszPreferredSrv + ) +/*++ + +Routine Description: + This routine gets the user's preferred server and print options from + the registry. + +Arguments: + + ppszPreferredSrv - Receives the user's preferred server + + +Return Value: + + Returns the appropriate Win32 error. + +--*/ +{ + + HKEY hKeyCurrentUser = NULL; + DWORD BufferSize; + DWORD BytesNeeded; + DWORD ValueType; + LPWSTR PreferredServer ; + DWORD err ; + + // + // get to right place in registry and allocate dthe buffer + // + if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser)) + { + // + // If somebody mess around with the registry and we can't find + // the registry, just use the defaults. + // + *ppszPreferredSrv = NULL; + return NO_ERROR; + } + + BufferSize = sizeof(WCHAR) * (MAX_PATH + 2) ; + PreferredServer = (LPWSTR) LocalAlloc(LPTR, BufferSize) ; + if (!PreferredServer) + return (GetLastError()) ; + + // + // Read PreferredServer value into Buffer. + // + BytesNeeded = BufferSize ; + + err = RegQueryValueExW( hKeyCurrentUser, + NW_SERVER_VALUENAME, + NULL, + &ValueType, + (LPBYTE) PreferredServer, + &BytesNeeded ); + + if (err != NO_ERROR) + { + // + // set to empty and carry on + // + PreferredServer[0] = 0; + } + + if (hKeyCurrentUser != NULL) + (void) RegCloseKey(hKeyCurrentUser) ; + *ppszPreferredSrv = PreferredServer ; + return NO_ERROR ; +} + + |