/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
provider.c
Abstract:
This module contains NetWare Network Provider code. It is the
client-side wrapper for APIs supported by the Workstation service.
Author:
Rita Wong (ritaw) 15-Feb-1993
Revision History:
Yi-Hsin Sung (yihsins) 10-July-1993
Moved all dialog handling to nwdlg.c
--*/
#include <nwclient.h>
#include <nwsnames.h>
#include <nwcanon.h>
#include <validc.h>
#include <nwevent.h>
#include <ntmsv1_0.h>
#include <nwdlg.h>
#include <nwreg.h>
#include <nwauth.h>
#include <mpr.h> // WNFMT_ manifests
#include <nwmisc.h>
#ifndef NT1057
#include <nwutil.h>
#endif
//-------------------------------------------------------------------//
// //
// Local Function Prototypes //
// //
//-------------------------------------------------------------------//
STATIC
BOOL
NwpWorkstationStarted(
VOID
);
STATIC
DWORD
NwpMapNameToUNC(
IN LPWSTR pszName,
OUT LPWSTR *ppszUNC
);
STATIC
VOID
NwpGetUncInfo(
IN LPWSTR lpstrUnc,
OUT WORD * slashCount,
OUT BOOL * isNdsUnc
);
STATIC
DWORD
NwpGetUncObjectName(
IN LPWSTR ContainerName
);
//-------------------------------------------------------------------//
// //
// Global variables //
// //
//-------------------------------------------------------------------//
#if DBG
DWORD NwProviderTrace = 0;
#endif
DWORD
APIENTRY
NPGetCaps(
IN DWORD QueryVal
)
/*++
Routine Description:
This function returns the functionality supported by this network
provider.
Arguments:
QueryVal - Supplies a value which determines the type of information
queried regarding the network provider's support in this area.
Return Value:
Returns a value which indicates the level of support given by this
provider.
--*/
{
#if DBG
IF_DEBUG(INIT) {
KdPrint(("\nNWPROVAU: NPGetCaps %lu\n", QueryVal));
}
#endif
switch (QueryVal) {
case WNNC_SPEC_VERSION:
return 0x00040000;
case WNNC_NET_TYPE:
return WNNC_NET_NETWARE ;
case WNNC_USER:
return WNNC_USR_GETUSER;
case WNNC_CONNECTION:
return (WNNC_CON_ADDCONNECTION |
WNNC_CON_ADDCONNECTION3 |
WNNC_CON_CANCELCONNECTION |
WNNC_CON_GETPERFORMANCE |
WNNC_CON_GETCONNECTIONS);
case WNNC_ENUMERATION:
return ( WNNC_ENUM_GLOBAL |
WNNC_ENUM_CONTEXT |
WNNC_ENUM_LOCAL );
case WNNC_START:
if (NwpWorkstationStarted()) {
return 1;
}
else {
return 0xffffffff; // don't know
}
case WNNC_DIALOG:
return WNNC_DLG_FORMATNETWORKNAME
#ifdef NT1057
;
#else
| WNNC_DLG_GETRESOURCEPARENT | WNNC_DLG_GETRESOURCEINFORMATION;
#endif
//
// The rest are not supported by the NetWare provider
//
default:
return 0;
}
}
#define NW_EVENT_MESSAGE_FILE L"nwevent.dll"
DWORD
APIENTRY
NPGetUser(
LPWSTR lpName,
LPWSTR lpUserName,
LPDWORD lpUserNameLen
)
/*++
Routine Description:
This is used to determine either the current default username, or the
username used to establish a network connection.
Arguments:
lpName - Contains the name of the local device the caller is interested
in, or a network name that the user has made a connection to. This
may be NULL or the empty string if the caller is interested in the
name of the user currently logged on to the system. If a network
name is passed in, and the user is connected to that resource using
different names, it is possible that a provider cannont resolve
which username to return. In this case the provider may make an
arbitrary choice amonst the possible usernames.
lpUserName - Points to a buffer to receive the user name. this should
be a name that can be passed into the NPAddConnection or
NPAddConnection3 function to re-establish the connection with the
same user name.
lpBufferSize - This is used to specify the size (in characters) of the
buffer passed in. If the call fails because the buffer is not big
enough, this location will be used to return the required buffer size.
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.
WN_NO_NETWORK - Network not present.
--*/
{
DWORD status;
DWORD dwUserNameBufferSize = *lpUserNameLen * sizeof(WCHAR);
DWORD CharsRequired = 0;
if (lpName == NULL)
{
return WN_NOT_CONNECTED;
}
RtlZeroMemory( lpUserName, dwUserNameBufferSize );
#if DBG
IF_DEBUG(CONNECT)
{
KdPrint(("\nNWPROVAU: NPGetUser %ws\n", lpName));
}
#endif
RpcTryExcept
{
status = NwrGetUser(
NULL,
lpName,
(LPBYTE) lpUserName,
dwUserNameBufferSize,
&CharsRequired
);
if (status == WN_MORE_DATA)
{
//
// Output buffer too small.
//
*lpUserNameLen = CharsRequired;
}
}
RpcExcept(1)
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR)
{
SetLastError(status);
}
return status;
}
DWORD
APIENTRY
NPAddConnection(
LPNETRESOURCEW lpNetResource,
LPWSTR lpPassword,
LPWSTR lpUserName
)
/*++
Routine Description:
This function creates a remote connection.
Arguments:
lpNetResource - Supplies the NETRESOURCE structure which specifies
the local DOS device to map, the remote resource to connect to
and other attributes related to the connection.
lpPassword - Supplies the password to connect with.
lpUserName - Supplies the username to connect with.
Return Value:
NO_ERROR - Successful.
WN_BAD_VALUE - Invalid value specifed in lpNetResource.
WN_BAD_NETNAME - Invalid remote resource name.
WN_BAD_LOCALNAME - Invalid local DOS device name.
WN_BAD_PASSWORD - Invalid password.
WN_ALREADY_CONNECTED - Local DOS device name is already in use.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
LPWSTR pszRemoteName = NULL;
UCHAR EncodeSeed = NW_ENCODE_SEED3;
UNICODE_STRING PasswordStr;
LPWSTR CachedUserName = NULL ;
LPWSTR CachedPassword = NULL ;
PasswordStr.Length = 0;
status = NwpMapNameToUNC(
lpNetResource->lpRemoteName,
&pszRemoteName );
if (status != NO_ERROR)
{
SetLastError(status);
return status;
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWPROVAU: NPAddConnection %ws\n", pszRemoteName));
}
#endif
RpcTryExcept
{
if (lpNetResource->dwType != RESOURCETYPE_ANY &&
lpNetResource->dwType != RESOURCETYPE_DISK &&
lpNetResource->dwType != RESOURCETYPE_PRINT)
{
status = WN_BAD_VALUE;
}
else
{
#ifdef NT1057
//
// no credentials specified, see if we have cached credentials
//
if (!lpPassword && !lpUserName)
{
(void) NwpRetrieveCachedCredentials(
pszRemoteName,
&CachedUserName,
&CachedPassword) ;
//
// these values will be NULL still if nothing found
//
lpPassword = CachedPassword ;
lpUserName = CachedUserName ;
}
#endif
//
// Encode password.
//
RtlInitUnicodeString(&PasswordStr, lpPassword);
RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
status = NwrCreateConnection(
NULL,
lpNetResource->lpLocalName,
pszRemoteName,
lpNetResource->dwType,
lpPassword,
lpUserName
);
if (CachedUserName)
{
(void)LocalFree((HLOCAL)CachedUserName);
}
if (CachedPassword)
{
RtlZeroMemory(CachedPassword,
wcslen(CachedPassword) *
sizeof(WCHAR)) ;
(void)LocalFree((HLOCAL)CachedPassword);
}
}
}
RpcExcept(1)
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (PasswordStr.Length != 0 && !CachedPassword)
{
//
// Restore password to original state
//
RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &PasswordStr);
}
if (status == ERROR_SHARING_PAUSED)
{
HMODULE MessageDll;
WCHAR Buffer[1024];
DWORD MessageLength;
DWORD err;
HKEY hkey;
LPWSTR pszProviderName = NULL;
//
// Load the netware message file DLL
//
MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
if (MessageDll == NULL)
{
goto ExitPoint ;
}
//
// Read the Network Provider Name.
//
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
// \NWCWorkstation\networkprovider
//
err = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_WORKSTATION_PROVIDER_PATH,
REG_OPTION_NON_VOLATILE, // options
KEY_READ, // desired access
&hkey
);
if ( !err )
{
//
// ignore the return code. if fail, pszProviderName is NULL
//
err = NwReadRegValue(
hkey,
NW_PROVIDER_VALUENAME,
&pszProviderName // free with LocalFree
);
RegCloseKey( hkey );
}
if (err)
{
(void) FreeLibrary(MessageDll);
goto ExitPoint ;
}
RtlZeroMemory(Buffer, sizeof(Buffer)) ;
//
// Get string from message file
//
MessageLength = FormatMessageW(
FORMAT_MESSAGE_FROM_HMODULE,
(LPVOID) MessageDll,
NW_LOGIN_DISABLED,
0,
Buffer,
sizeof(Buffer) / sizeof(WCHAR),
NULL
);
if (MessageLength != 0)
{
status = WN_EXTENDED_ERROR ;
WNetSetLastErrorW(NW_LOGIN_DISABLED,
Buffer,
pszProviderName) ;
}
(void) LocalFree( (HLOCAL) pszProviderName );
(void) FreeLibrary(MessageDll);
}
ExitPoint:
if (status != NO_ERROR)
{
SetLastError(status);
}
LocalFree( (HLOCAL) pszRemoteName );
return status;
}
DWORD
APIENTRY
NPAddConnection3(
HWND hwndOwner,
LPNETRESOURCEW lpNetResource,
LPWSTR lpPassword,
LPWSTR lpUserName,
DWORD dwConnFlags
)
/*++
Routine Description:
This function creates a remote connection.
Arguments:
hwndOwner - Owner window handle for dialog boxes
lpNetResource - Supplies the NETRESOURCE structure which specifies
the local DOS device to map, the remote resource to connect to
and other attributes related to the connection.
lpPassword - Supplies the password to connect with.
lpUserName - Supplies the username to connect with.
dwConnFlags - CONNECT_UPDATE_PROFILE...
Return Value:
NO_ERROR - Successful.
WN_BAD_VALUE - Invalid value specifed in lpNetResource.
WN_BAD_NETNAME - Invalid remote resource name.
WN_BAD_LOCALNAME - Invalid local DOS device name.
WN_BAD_PASSWORD - Invalid password.
WN_ALREADY_CONNECTED - Local DOS device name is already in use.
Other network errors.
--*/
{
DWORD err = NO_ERROR;
LPWSTR UserName = NULL;
LPWSTR Password = NULL;
if ( ( dwConnFlags & CONNECT_PROMPT )
&& !( dwConnFlags & CONNECT_INTERACTIVE )
)
{
return WN_BAD_VALUE;
}
if ( !(dwConnFlags & CONNECT_PROMPT ))
{
err = NPAddConnection( lpNetResource,
lpPassword,
lpUserName );
if ( ( err == NO_ERROR )
|| !( dwConnFlags & CONNECT_INTERACTIVE ) // Cannot popup dialog
)
{
return err;
}
}
for (;;)
{
if ( ( err != NO_ERROR ) // CONNECT_PROMPT
&& ( err != WN_BAD_PASSWORD )
&& ( err != WN_ACCESS_DENIED )
&& ( err != ERROR_NO_SUCH_USER )
)
{
// Errors not related to access problems
break;
}
if ( UserName )
{
(void) LocalFree( UserName );
UserName = NULL;
}
if ( Password )
{
memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
(void) LocalFree( Password );
Password = NULL;
}
//
// Put up dialog to get username
// and password.
//
err = NwpGetUserCredential( hwndOwner,
lpNetResource->lpRemoteName,
err,
lpUserName,
&UserName,
&Password );
if ( err != NO_ERROR )
break;
err = NPAddConnection( lpNetResource,
Password,
UserName );
if ( err == NO_ERROR )
{
#if 0
if ( (UserName != NULL) && (Password != NULL))
{
// Checking UserName and Password is to make sure that
// we have prompted for password
(VOID) NwpCacheCredentials( lpNetResource->lpRemoteName,
UserName,
Password ) ;
}
#endif
break;
}
}
if ( UserName )
(void) LocalFree( UserName );
if ( Password )
{
memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
(void) LocalFree( Password );
}
return err;
}
DWORD
APIENTRY
NPCancelConnection(
LPWSTR lpName,
BOOL fForce
)
/*++
Routine Description:
This function deletes a remote connection.
Arguments:
lpName - Supplies the local DOS device, or the remote resource name
if it is a UNC connection to delete.
fForce - Supplies the force level to break the connection. TRUE means
to forcefully delete the connection, FALSE means end the connection
only if there are no opened files.
Return Value:
NO_ERROR - Successful.
WN_BAD_NETNAME - Invalid remote resource name.
WN_NOT_CONNECTED - Connection could not be found.
WN_OPEN_FILES - fForce is FALSE and there are opened files on the
connection.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
LPWSTR pszName = NULL;
//
// We only need to map remote resource name
//
if ( NwLibValidateLocalName( lpName ) != NO_ERROR )
{
status = NwpMapNameToUNC(
lpName,
&pszName
);
if (status != NO_ERROR) {
SetLastError(status);
return status;
}
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWPROVAU: NPCancelConnection %ws, Force %u\n",
pszName? pszName : lpName, fForce));
}
#endif
RpcTryExcept {
status = NwrDeleteConnection(
NULL,
pszName? pszName : lpName,
(DWORD) fForce
);
}
RpcExcept(1) {
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR) {
SetLastError(status);
}
LocalFree( (HLOCAL) pszName );
return status;
}
DWORD
APIENTRY
NPGetConnection(
LPWSTR lpLocalName,
LPWSTR lpRemoteName,
LPDWORD lpnBufferLen
)
/*++
Routine Description:
This function returns the remote resource name for a given local
DOS device.
Arguments:
lpLocalName - Supplies the local DOS device to look up.
lpRemoteName - Output buffer to receive the remote resource name
mapped to lpLocalName.
lpnBufferLen - On input, supplies length of the lpRemoteName buffer
in number of characters. On output, if error returned is
WN_MORE_DATA, receives the number of characters required of
the output buffer to hold the output string.
Return Value:
NO_ERROR - Successful.
WN_BAD_LOCALNAME - Invalid local DOS device.
WN_NOT_CONNECTED - Connection could not be found.
WN_MORE_DATA - Output buffer is too small.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
DWORD CharsRequired;
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWPROVAU: NPGetConnection %ws\n", lpLocalName));
}
#endif
RpcTryExcept {
if (lpRemoteName && *lpnBufferLen)
*lpRemoteName = 0 ;
status = NwrQueryServerResource(
NULL,
lpLocalName,
(*lpnBufferLen == 0? NULL : lpRemoteName),
*lpnBufferLen,
&CharsRequired
);
if (status == ERROR_INSUFFICIENT_BUFFER)
status = WN_MORE_DATA;
if (status == WN_MORE_DATA) {
*lpnBufferLen = CharsRequired;
}
}
RpcExcept(1) {
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR) {
SetLastError(status);
}
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWPROVAU: NPGetConnection returns %lu\n", status));
if (status == NO_ERROR) {
KdPrint((" %ws, BufferLen %lu, CharsRequired %lu\n", lpRemoteName, *lpnBufferLen, CharsRequired));
}
}
#endif
return status;
}
DWORD
APIENTRY
NPGetConnectionPerformance(
LPCWSTR lpRemoteName,
LPNETCONNECTINFOSTRUCT lpNetConnectInfo
)
/*++
Routine Description:
This function returns information about the expected performance of a
connection used to access a network resource. The request can only be
for a network resource to which there is currently a connection.
Arguments:
lpRemoteName - Contains the local name or remote name for a resource
for which a connection exists.
lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
which is to be filled if the connection performance
of connection lpRemoteName can be determined.
Return Value:
NO_ERROR - Successful.
WN_NOT_CONNECTED - Connection could not be found.
WN_NONETWORK - Network is not present.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
LPWSTR pszRemoteName;
if ( lpNetConnectInfo == NULL )
{
status = ERROR_INVALID_PARAMETER;
SetLastError(status);
return status;
}
pszRemoteName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
( wcslen(lpRemoteName) + 1 ) *
sizeof(WCHAR) );
if ( pszRemoteName == NULL )
{
status = ERROR_NOT_ENOUGH_MEMORY;
SetLastError(status);
return status;
}
wcscpy( pszRemoteName, lpRemoteName );
_wcsupr( pszRemoteName );
#if DBG
IF_DEBUG(CONNECT) {
KdPrint(("\nNWPROVAU: NPGetConnectionPerformance %ws\n", pszRemoteName));
}
#endif
RpcTryExcept {
status = NwrGetConnectionPerformance(
NULL,
pszRemoteName,
(LPBYTE) lpNetConnectInfo,
sizeof(NETCONNECTINFOSTRUCT) );
}
RpcExcept(1)
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR)
{
SetLastError(status);
}
LocalFree( (HLOCAL) pszRemoteName );
return status;
}
DWORD
APIENTRY
NPGetUniversalName(
#ifdef NT1057
LPWSTR lpLocalPath,
#else
LPCWSTR lpLocalPath,
#endif
DWORD dwInfoLevel,
LPVOID lpBuffer,
LPDWORD lpBufferSize
)
/*++
Routine Description:
This function returns the universal resource name for a given local
path.
Arguments:
lpLocalPath - Supplies the local DOS Path to look up.
dwInfoLevel - Info level requested.
lpBuffer - Output buffer to receive the appropruatye structure.
lpBufferLen - On input, supplies length of the buffer in number of
bytes. On output, if error returned is WN_MORE_DATA, receives
the number of bytes required of the output buffer.
Return Value:
NO_ERROR - Successful.
WN_BAD_LOCALNAME - Invalid local DOS device.
WN_NOT_CONNECTED - Connection could not be found.
WN_MORE_DATA - Output buffer is too small.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
DWORD dwCharsRequired = MAX_PATH + 1 ;
DWORD dwBytesNeeded ;
DWORD dwLocalLength ;
LPWSTR lpRemoteBuffer ;
WCHAR szDrive[3] ;
//
// check for bad info level
//
if ((dwInfoLevel != UNIVERSAL_NAME_INFO_LEVEL) &&
(dwInfoLevel != REMOTE_NAME_INFO_LEVEL))
{
return WN_BAD_VALUE ;
}
//
// check for bad pointers
//
if (!lpLocalPath || !lpBuffer || !lpBufferSize)
{
return WN_BAD_POINTER ;
}
//
// local path must at least have "X:"
//
if (((dwLocalLength = wcslen(lpLocalPath)) < 2) ||
(lpLocalPath[1] != L':') ||
((dwLocalLength > 2) && (lpLocalPath[2] != L'\\')))
{
return WN_BAD_VALUE ;
}
//
// preallocate some memory
//
if (!(lpRemoteBuffer = (LPWSTR) LocalAlloc(
LPTR,
dwCharsRequired * sizeof(WCHAR))))
{
status = GetLastError() ;
goto ErrorExit ;
}
szDrive[2] = 0 ;
wcsncpy(szDrive, lpLocalPath, 2) ;
//
// get the remote path by calling the existing API
//
status = NPGetConnection(
szDrive,
lpRemoteBuffer,
&dwCharsRequired) ;
if (status == WN_MORE_DATA)
{
//
// reallocate the correct size
//
if (!(lpRemoteBuffer = (LPWSTR) LocalReAlloc(
(HLOCAL) lpRemoteBuffer,
dwCharsRequired * sizeof(WCHAR),
LMEM_MOVEABLE)))
{
status = GetLastError() ;
goto ErrorExit ;
}
status = NPGetConnection(
szDrive,
lpRemoteBuffer,
&dwCharsRequired) ;
}
if (status != WN_SUCCESS)
{
goto ErrorExit ;
}
//
// at minimum we will need this size of the UNC name
// the -2 is because we loose the drive letter & colon.
//
dwBytesNeeded = (wcslen(lpRemoteBuffer) +
dwLocalLength - 2 + 1) * sizeof(WCHAR) ;
switch (dwInfoLevel)
{
case UNIVERSAL_NAME_INFO_LEVEL:
{
LPUNIVERSAL_NAME_INFO lpUniversalNameInfo ;
//
// calculate how many bytes we really need
//
dwBytesNeeded += sizeof(UNIVERSAL_NAME_INFO) ;
if (*lpBufferSize < dwBytesNeeded)
{
*lpBufferSize = dwBytesNeeded ;
status = WN_MORE_DATA ;
break ;
}
//
// now we are all set. just stick the data in the buffer
//
lpUniversalNameInfo = (LPUNIVERSAL_NAME_INFO) lpBuffer ;
lpUniversalNameInfo->lpUniversalName = (LPWSTR)
(((LPBYTE)lpBuffer) + sizeof(UNIVERSAL_NAME_INFO)) ;
wcscpy(lpUniversalNameInfo->lpUniversalName,
lpRemoteBuffer) ;
wcscat(lpUniversalNameInfo->lpUniversalName,
lpLocalPath+2) ;
break ;
}
case REMOTE_NAME_INFO_LEVEL :
{
LPREMOTE_NAME_INFO lpRemoteNameInfo ;
//
// calculate how many bytes we really need
//
dwBytesNeeded *= 2 ; // essentially twice the info + terminator
dwBytesNeeded += (sizeof(REMOTE_NAME_INFO) + sizeof(WCHAR)) ;
if (*lpBufferSize < dwBytesNeeded)
{
*lpBufferSize = dwBytesNeeded ;
status = WN_MORE_DATA ;
break ;
}
//
// now we are all set. just stick the data in the buffer
//
lpRemoteNameInfo = (LPREMOTE_NAME_INFO) lpBuffer ;
lpRemoteNameInfo->lpUniversalName = (LPWSTR)
(((LPBYTE)lpBuffer) + sizeof(REMOTE_NAME_INFO)) ;
wcscpy(lpRemoteNameInfo->lpUniversalName,
lpRemoteBuffer) ;
wcscat(lpRemoteNameInfo->lpUniversalName,
lpLocalPath+2) ;
lpRemoteNameInfo->lpConnectionName =
lpRemoteNameInfo->lpUniversalName +
wcslen(lpRemoteNameInfo->lpUniversalName) + 1 ;
wcscpy(lpRemoteNameInfo->lpConnectionName,
lpRemoteBuffer) ;
lpRemoteNameInfo->lpRemainingPath =
lpRemoteNameInfo->lpConnectionName +
wcslen(lpRemoteNameInfo->lpConnectionName) + 1 ;
wcscpy(lpRemoteNameInfo->lpRemainingPath,
lpLocalPath+2) ;
break ;
}
default:
//
// yikes!
//
status = WN_BAD_VALUE ;
ASSERT(FALSE);
}
ErrorExit:
if (lpRemoteBuffer)
{
(void) LocalFree((HLOCAL)lpRemoteBuffer) ;
}
return status;
}
DWORD
APIENTRY
NPOpenEnum(
DWORD dwScope,
DWORD dwType,
DWORD dwUsage,
LPNETRESOURCEW lpNetResource,
LPHANDLE lphEnum
)
/*++
Routine Description:
This function initiates an enumeration of either connections, or
browsing of network resource.
Arguments:
dwScope - Supplies the category of enumeration to do--either
connection or network browsing.
dwType - Supplies the type of resource to get--either disk,
print, or it does not matter.
dwUsage - Supplies the object type to get--either container,
or connectable usage.
lpNetResource - Supplies, in the lpRemoteName field, the container
name to enumerate under.
lphEnum - Receives the resumable context handle to be used on all
subsequent calls to get the list of objects under the container.
Return Value:
NO_ERROR - Successful.
WN_BAD_VALUE - Either the dwScope, dwType, or the dwUsage specified
is not acceptable.
WN_BAD_NETNAME - Invalid remote resource name.
WN_NOT_CONTAINER - Remote resource name is not a container.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
#if DBG
IF_DEBUG(ENUM)
{
KdPrint(("\nNWPROVAU: NPOpenEnum\n"));
}
#endif
RpcTryExcept
{
if ( ( dwType & RESOURCETYPE_DISK ) ||
( dwType & RESOURCETYPE_PRINT ) ||
( dwType == RESOURCETYPE_ANY ) )
{
switch ( dwScope )
{
case RESOURCE_CONNECTED:
status = NwrOpenEnumConnections( NULL,
dwType,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
break;
case RESOURCE_CONTEXT:
status = NwrOpenEnumContextInfo( NULL,
dwType,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
break;
case RESOURCE_GLOBALNET:
if ( lpNetResource == NULL )
{
//
// Enumerating servers and NDS trees
//
if ( dwUsage & RESOURCEUSAGE_CONTAINER || dwUsage == 0 )
{
status = NwrOpenEnumServersAndNdsTrees( NULL,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else
{
//
// There is no such thing as a connectable server
// object.
//
status = WN_BAD_VALUE;
}
}
else
{
BOOL IsEnumVolumes = TRUE;
LPWSTR pszRemoteName = NULL;
WORD slashCount;
BOOL isNdsUnc;
NwpGetUncInfo( lpNetResource->lpRemoteName,
&slashCount,
&isNdsUnc );
//
// Either enumerating volumes, directories, or NDS subtrees
//
if ( dwUsage & RESOURCEUSAGE_CONNECTABLE ||
dwUsage & RESOURCEUSAGE_CONTAINER ||
dwUsage == 0 )
{
LPWSTR tempStrPtr = lpNetResource->lpRemoteName;
DWORD dwClassType = 0;
if ( tempStrPtr[0] == L' ' &&
tempStrPtr[1] == L'\\' &&
tempStrPtr[2] == L'\\' )
tempStrPtr = &tempStrPtr[1];
if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
{
if ( ( dwType == RESOURCETYPE_ANY ) ||
( ( dwType & RESOURCETYPE_DISK ) &&
( dwType & RESOURCETYPE_PRINT ) ) )
{
status = NwrOpenEnumNdsSubTrees_Any( NULL,
tempStrPtr,
NULL,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else if ( dwType & RESOURCETYPE_DISK )
{
status = NwrOpenEnumNdsSubTrees_Disk( NULL,
tempStrPtr,
NULL,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else if ( dwType & RESOURCETYPE_PRINT )
{
status = NwrOpenEnumNdsSubTrees_Print( NULL,
tempStrPtr,
NULL,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else
{
KdPrint(("NWOpenEnum: Unhandled dwType %lu\n", dwType));
}
}
else if (
( slashCount < 4 ) &&
( ( dwType == RESOURCETYPE_ANY ) ||
( ( dwType & RESOURCETYPE_DISK ) &&
( dwType & RESOURCETYPE_PRINT ) ) ) &&
( ( status = NwrOpenEnumNdsSubTrees_Any( NULL,
tempStrPtr,
&dwClassType,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
==NO_ERROR )
)
{
status = NO_ERROR;
}
else if (
( slashCount < 4 ) &&
( dwType & RESOURCETYPE_DISK ) &&
( ( status = NwrOpenEnumNdsSubTrees_Disk( NULL,
tempStrPtr,
&dwClassType,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
==NO_ERROR )
)
{
status = NO_ERROR;
}
else if (
( slashCount < 4 ) &&
( dwType & RESOURCETYPE_PRINT ) &&
( ( status = NwrOpenEnumNdsSubTrees_Print( NULL,
tempStrPtr,
&dwClassType,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
==NO_ERROR )
)
{
status = NO_ERROR;
}
else if (
(slashCount < 4
&&
(status == ERROR_NETWORK_ACCESS_DENIED
||
status == ERROR_GEN_FAILURE
||
status == ERROR_ACCESS_DENIED
||
status == ERROR_BAD_NETPATH
||
status == WN_BAD_NETNAME
||
status == ERROR_INVALID_NAME))
||
( slashCount > 3 && status == NO_ERROR )
)
{
if (( status == ERROR_NETWORK_ACCESS_DENIED ) &&
( dwClassType == CLASS_TYPE_NCP_SERVER ))
{
status = NO_ERROR;
isNdsUnc = TRUE;
IsEnumVolumes = TRUE;
}
else if ( ( status == ERROR_NETWORK_ACCESS_DENIED ) &&
( ( dwClassType == CLASS_TYPE_VOLUME ) ||
( dwClassType == CLASS_TYPE_DIRECTORY_MAP ) ) )
{
status = NO_ERROR;
isNdsUnc = TRUE;
IsEnumVolumes = FALSE;
}
else
{
//
// A third backslash means that we want to
// enumerate the directories.
//
if ( isNdsUnc && slashCount > 3 )
IsEnumVolumes = FALSE;
if ( !isNdsUnc && slashCount > 2 )
IsEnumVolumes = FALSE;
if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE )
IsEnumVolumes = FALSE;
}
status = NwpMapNameToUNC( tempStrPtr,
&pszRemoteName );
if ( status == NO_ERROR )
{
if ( IsEnumVolumes )
{
LPWSTR pszServerName = pszRemoteName;
// BUGBUG - The following 10 lines are a hack to
// allow the provider to browse past the CN=<server>
// object in an NDS tree.
if ( slashCount == 3 && isNdsUnc == TRUE )
{
pszServerName = (LPWSTR)
NwpGetUncObjectName( pszRemoteName );
if ( pszServerName == NULL )
pszServerName = pszRemoteName;
}
else if ( dwUsage & RESOURCEUSAGE_ATTACHED )
{
#ifndef NT1057
// This is a bindery server.
// Return WN_NOT_AUTHENTICATED if
// we are not already attached so
// that clients ( explorer ) will
// do NPAddConnection3 to make
// a connection to the server.
BOOL fAttached;
BOOL fAuthenticated;
status = NwIsServerOrTreeAttached(
pszServerName + 2,
&fAttached,
&fAuthenticated );
if ( status != NO_ERROR )
break;
if ( !fAttached || !fAuthenticated)
{
// See if the server belongs to
// our provider.
status = NwrOpenEnumVolumes(
NULL,
pszServerName,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
if ( status == NO_ERROR )
{
// The server belongs to us.
// Close the handle and
// return not attached if
// callee passed in dwUsage
// flag:
// RESOURCEUSAGE_ATTACHED.
// Note: handle will be null
// after return from
// NwrCloseEnum
NwrCloseEnum( (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
status = WN_NOT_AUTHENTICATED;
}
else
{
// else the server does not
// belong to us.
status = WN_BAD_NETNAME;
}
break;
}
#endif
} // else, this is a bindery server and
// client does not care whether we
// are bindery authenticated.
if ( ( dwType == RESOURCETYPE_ANY ) ||
( ( dwType & RESOURCETYPE_DISK ) &&
( dwType & RESOURCETYPE_PRINT ) ) )
{
status = NwrOpenEnumVolumesQueues(
NULL,
pszServerName,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else if ( dwType & RESOURCETYPE_DISK )
{
status = NwrOpenEnumVolumes(
NULL,
pszServerName,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
else if ( dwType & RESOURCETYPE_PRINT )
{
status = NwrOpenEnumQueues(
NULL,
pszServerName,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
}
}
else
{
LPWSTR CachedUserName = NULL ;
LPWSTR CachedPassword = NULL ;
#ifdef NT1057 // Make OpenEnum not interactive on SUR
(void) NwpRetrieveCachedCredentials( pszRemoteName,
&CachedUserName,
&CachedPassword );
#endif
status = NwrOpenEnumDirectories(
NULL,
pszRemoteName,
CachedUserName,
CachedPassword,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
#ifndef NT1057 // Make OpenEnum not interactive on SUR
if ( (status == ERROR_INVALID_PASSWORD)
|| (status == ERROR_NO_SUCH_USER )
)
{
status = WN_NOT_AUTHENTICATED;
break;
}
#else
if ( CachedUserName )
{
(void) LocalFree( (HLOCAL) CachedUserName );
}
if ( CachedPassword )
{
RtlZeroMemory( CachedPassword,
wcslen(CachedPassword) *
sizeof( WCHAR ) );
(void) LocalFree( ( HLOCAL ) CachedPassword );
}
if ( ( status == ERROR_INVALID_PASSWORD ) ||
( status == ERROR_NO_SUCH_USER ) )
{
LPWSTR UserName;
LPWSTR Password;
LPWSTR TmpPtr;
//
// Put up dialog to get username
// and password.
//
status = NwpGetUserCredential( NULL,
tempStrPtr,
status,
NULL,
&UserName,
&Password);
if ( status == NO_ERROR )
{
status = NwrOpenEnumDirectories(
NULL,
pszRemoteName,
UserName,
Password,
(LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
if ( status == NO_ERROR )
{
status = NwpCacheCredentials(
pszRemoteName,
UserName,
Password ) ;
}
(void) LocalFree( UserName );
//
// Clear the password
//
TmpPtr = Password;
while ( *TmpPtr != 0 )
*TmpPtr++ = 0;
(void) LocalFree( Password );
}
else if ( status == ERROR_WINDOW_NOT_DIALOG )
{
//
// Caller is not a GUI app.
//
status = ERROR_INVALID_PASSWORD;
}
else if ( status == WN_CANCEL )
{
//
// Cancel was pressed but we still
// have to return success or MPR
// will popup the error. Return
// a bogus enum handle.
//
*lphEnum = (HANDLE) 0xFFFFFFFF;
status = NO_ERROR;
}
}
#endif
}
}
else
{
status = WN_BAD_NETNAME;
}
}
}
else
{
status = WN_BAD_VALUE;
}
if ( pszRemoteName != NULL )
LocalFree( (HLOCAL) pszRemoteName );
}
break;
default:
KdPrint(("NWPROVIDER: Invalid dwScope %lu\n", dwScope));
status = WN_BAD_VALUE;
} // end switch
}
else
{
status = WN_BAD_VALUE;
}
}
RpcExcept( 1 )
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if ( status == ERROR_FILE_NOT_FOUND )
status = WN_BAD_NETNAME;
if ( status != NO_ERROR )
{
SetLastError( status );
}
return status;
}
DWORD
APIENTRY
NPEnumResource(
HANDLE hEnum,
LPDWORD lpcCount,
LPVOID lpBuffer,
LPDWORD lpBufferSize
)
/*++
Routine Description:
This function returns a lists of objects within the container
specified by the enumeration context handle.
Arguments:
hEnum - Supplies the resumable enumeration context handle.
NOTE: If this value is 0xFFFFFFFF, it is not a context
handle and this routine is required to return
WN_NO_MORE_ENTRIES. This hack is to handle the
case where the user cancelled out of the network
credential dialog on NwrOpenEnumDirectories and we
cannot return an error there or we generate an error
popup.
lpcCount - On input, supplies the number of entries to get.
On output, if NO_ERROR is returned, receives the number
of entries NETRESOURCE returned in lpBuffer.
lpBuffer - Receives an array of NETRESOURCE entries, each
entry describes an object within the container.
lpBufferSize - On input, supplies the size of lpBuffer in
bytes. On output, if WN_MORE_DATA is returned, receives
the number of bytes needed in the buffer to get the
next entry.
Return Value:
NO_ERROR - Successfully returned at least one entry.
WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
is returned.
WN_MORE_DATA - lpBuffer is too small to even get one entry.
WN_BAD_HANDLE - The enumeration handle is invalid.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
DWORD BytesNeeded = 0;
DWORD EntriesRead = 0;
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWPROVAU: NPEnumResource\n"));
}
#endif
RpcTryExcept {
if (hEnum == (HANDLE) 0xFFFFFFFF) {
status = WN_NO_MORE_ENTRIES;
goto EndOfTry;
}
status = NwrEnum(
(NWWKSTA_CONTEXT_HANDLE) hEnum,
*lpcCount,
(LPBYTE) lpBuffer,
*lpBufferSize,
&BytesNeeded,
&EntriesRead
);
if (status == WN_MORE_DATA) {
//
// Output buffer too small to fit a single entry.
//
*lpBufferSize = BytesNeeded;
}
else if (status == NO_ERROR) {
*lpcCount = EntriesRead;
}
EndOfTry: ;
}
RpcExcept(1) {
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
SetLastError(status);
}
else
{
//
// Convert offsets of strings to pointers
//
if (EntriesRead > 0) {
DWORD i;
LPNETRESOURCEW NetR;
NetR = lpBuffer;
for (i = 0; i < EntriesRead; i++, NetR++) {
if (NetR->lpLocalName != NULL) {
NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpLocalName);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpRemoteName);
if (NetR->lpComment != NULL) {
NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpComment);
}
if (NetR->lpProvider != NULL) {
NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpProvider);
}
}
}
}
return status;
}
DWORD
APIENTRY
NPGetResourceInformation(
LPNETRESOURCEW lpNetResource,
LPVOID lpBuffer,
LPDWORD cbBuffer,
LPWSTR * lplpSystem
)
/*++
Routine Description:
This function returns an object which details information
about a specified network resource.
Arguments:
lpNetResource - This specifies the network resource for which the
information is required. The lpRemoteName field of the NETRESOURCE
specifies the remote name of the network resource whose information
is required. If the calling program knows the values for the
lpProvider and dwType fields, then it should fill them in, otherwise,
it should set them to NULL. All other fields in the NETRESOURCE are
ignored and are not initialized.
lpBuffer - A pointer to the buffer to receive the result, which is
returned as a single NETRESOURCE entry representing the parent
resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
are returned, all other fields being set to NULL. The remote name
returned should be in the same syntax as that returned from an
enumeration, so that the caller can do a case sensitive string
compare to determine whether an enumerated resource is this resource.
If the provider owns a parent of the network resource, (in other
words is known to be the correct network to respond to this request),
then lpProvider should be filled in with a non-null entry. If it is
known that a network owns a parent of the resource, but that the
resource itself is not valid, then lpProvider is returned as a
non-null value together with a return status of WN_BAD_VALUE. dwScope
is returned as RESOURCE_CONTEXT if the network resource is part of
the user's network context, otherwise it is returned as zero.
cbBuffer - This specifies the size in bytes of the buffer passed to the
function call. If the result is WN_MORE_DATA, this will contain the
buffer size required (in bytes) to hold the NETRESOURCE information.
lplpSystem - Returned pointer to a string in the buffer pointed to by
lpBuffer that specifies the part of the resource that is accessed
through resource type specific system APIs rather than WNet APIs.
For example, if the input remote resource name was
"\\server\share\dir", then lpRemoteName is returned pointing to
"\\server\share" and lplpSystem points to "\dir", both strings
being stored in the buffer pointed to by 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.
--*/
{
DWORD status;
LPWSTR pszRemoteName = NULL;
DWORD BytesNeeded = 0;
DWORD SystemOffset = 0;
*lplpSystem = NULL;
status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
if (status != NO_ERROR)
{
SetLastError(status);
return status;
}
#if DBG
IF_DEBUG(CONNECT)
{
KdPrint(("\nNWPROVAU: NPGetResourceInformation %ws\n", pszRemoteName));
}
#endif
RpcTryExcept
{
if (lpNetResource->dwType != RESOURCETYPE_ANY &&
lpNetResource->dwType != RESOURCETYPE_DISK &&
lpNetResource->dwType != RESOURCETYPE_PRINT)
{
status = WN_BAD_VALUE;
}
else
{
status = NwrGetResourceInformation(
NULL,
pszRemoteName,
lpNetResource->dwType,
(LPBYTE) lpBuffer,
*cbBuffer,
&BytesNeeded,
&SystemOffset
);
if (status == WN_MORE_DATA)
{
//
// Output buffer too small.
//
*cbBuffer = BytesNeeded;
}
}
}
RpcExcept(1)
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if ( pszRemoteName )
LocalFree( (HLOCAL) pszRemoteName );
if (status != NO_ERROR)
{
SetLastError(status);
}
else
{
//
// Convert offsets of strings to pointers
//
DWORD i;
LPNETRESOURCEW NetR = lpBuffer;
if (NetR->lpLocalName != NULL)
{
NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpLocalName);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpRemoteName);
if (NetR->lpComment != NULL)
{
NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpComment);
}
if (NetR->lpProvider != NULL)
{
NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpProvider);
}
if (SystemOffset != 0)
{
*lplpSystem = (LPWSTR) ((DWORD) lpBuffer + SystemOffset);
}
}
return status;
}
DWORD
APIENTRY
NPGetResourceParent(
LPNETRESOURCEW lpNetResource,
LPVOID lpBuffer,
LPDWORD cbBuffer
)
/*++
Routine Description:
This function returns an object which details information
about the parent of a specified network resource.
Arguments:
lpNetResource - This specifies the network resource for which the
parent name is required. The NETRESOURCE could have been obtained via
previous NPEnumResource, or constructed by the caller. The lpRemoteName
field of the NETRESOURCE specifies the remote name of the network
resouce whose parent name is required. If the calling program knows
the values for the lpProvider and dwType fields, then it can fill
them in, otherwise, they are set to NULL. If the lpProvider field is
not NULL, then the network provider DLL can assume that the resource
is owned by its network, but if it is NULL, then it must assume
that the resource could be for some other network and do whatever
checking is neccessary to ensure that the result returned is accurate.
For example, if being asked for the parent of a server, and the server
is not part of a workgroup, the the network provider DLL should check
to ensure that the server is part of its network and, if so, return
its provider name. All other fields in the NETRESOURCE are ignored and
are not initialized.
lpBuffer - A pointer to the buffer to receive the result, which is
returned as a single NETRESOURCE entry representing the parent
resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
are returned, all other fields being set to NULL. lpProvider should
be set to NULL if the provider has only done a syntactic check (i.e.
does not know that the resource is specific to its network). If the
provider owns a parent of the network resource, (in other words is
known to be the correct network to respond to this request), then
lpProvider should be filled in with a non-null entry, even if the
return is WN_BAD_VALUE. The remote name returned should be in the
same syntax as that returned from an enumeration, so that the caller
can do a case sensitive string compare to determine whether an
enumerated resource is this resource. If a resource has no browse
parent on the network, the lpRemoteName is returned as NULL. The
RESOURCEUSAGE_CONNECTABLE value in the dwUsage field does not
indicate that the resource can currently be connected to, but that
the resource is connectable when it is available on the network.
cbBuffer - This specifies the size in bytes of the buffer passed to the
function call. If the result is WN_MORE_DATA, this will contain the
buffer size required (in bytes) to hold the NETRESOURCE information.
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).
--*/
{
DWORD status;
LPWSTR pszRemoteName = NULL;
DWORD BytesNeeded = 0;
status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
if (status != NO_ERROR)
{
SetLastError(status);
return status;
}
#if DBG
IF_DEBUG(CONNECT)
{
KdPrint(("\nNWPROVAU: NPGetResourceParent %ws\n", pszRemoteName));
}
#endif
RpcTryExcept
{
if (lpNetResource->dwType != RESOURCETYPE_ANY &&
lpNetResource->dwType != RESOURCETYPE_DISK &&
lpNetResource->dwType != RESOURCETYPE_PRINT)
{
status = WN_BAD_VALUE;
}
else
{
status = NwrGetResourceParent(
NULL,
pszRemoteName,
lpNetResource->dwType,
(LPBYTE) lpBuffer,
*cbBuffer,
&BytesNeeded
);
if (status == WN_MORE_DATA)
{
//
// Output buffer too small.
//
*cbBuffer = BytesNeeded;
}
}
}
RpcExcept(1)
{
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if ( pszRemoteName )
LocalFree( (HLOCAL) pszRemoteName );
if (status != NO_ERROR)
{
SetLastError(status);
}
else
{
//
// Convert offsets of strings to pointers
//
DWORD i;
LPNETRESOURCEW NetR = lpBuffer;
if (NetR->lpLocalName != NULL)
{
NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpLocalName);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpRemoteName);
if (NetR->lpComment != NULL)
{
NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpComment);
}
if (NetR->lpProvider != NULL)
{
NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpProvider);
}
}
return status;
}
DWORD
APIENTRY
NwEnumConnections(
HANDLE hEnum,
LPDWORD lpcCount,
LPVOID lpBuffer,
LPDWORD lpBufferSize,
BOOL fImplicitConnections
)
/*++
Routine Description:
This function returns a lists of connections.
Arguments:
hEnum - Supplies the resumable enumeration context handle.
NOTE: If this value is 0xFFFFFFFF, it is not a context
handle and this routine is required to return
WN_NO_MORE_ENTRIES. This hack is to handle the
case where the user cancelled out of the network
credential dialog on NwrOpenEnumDirectories and we
cannot return an error there or we generate an error
popup.
lpcCount - On input, supplies the number of entries to get.
On output, if NO_ERROR is returned, receives the number
of entries NETRESOURCE returned in lpBuffer.
lpBuffer - Receives an array of NETRESOURCE entries, each
entry describes an object within the container.
lpBufferSize - On input, supplies the size of lpBuffer in
bytes. On output, if WN_MORE_DATA is returned, receives
the number of bytes needed in the buffer to get the
next entry.
fImplicitConnections - TRUE is we also want all implicit connections,
FALSE otherwise.
Return Value:
NO_ERROR - Successfully returned at least one entry.
WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
is returned.
WN_MORE_DATA - lpBuffer is too small to even get one entry.
WN_BAD_HANDLE - The enumeration handle is invalid.
Other network errors.
--*/
{
DWORD status = NO_ERROR;
DWORD BytesNeeded = 0;
DWORD EntriesRead = 0;
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWPROVAU: NPEnumResource\n"));
}
#endif
RpcTryExcept {
if (hEnum == (HANDLE) 0xFFFFFFFF) {
status = WN_NO_MORE_ENTRIES;
goto EndOfTry;
}
status = NwrEnumConnections(
(NWWKSTA_CONTEXT_HANDLE) hEnum,
*lpcCount,
(LPBYTE) lpBuffer,
*lpBufferSize,
&BytesNeeded,
&EntriesRead,
fImplicitConnections
);
if (status == WN_MORE_DATA) {
//
// Output buffer too small to fit a single entry.
//
*lpBufferSize = BytesNeeded;
}
else if (status == NO_ERROR) {
*lpcCount = EntriesRead;
}
EndOfTry: ;
}
RpcExcept(1) {
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
SetLastError(status);
}
//
// Convert offsets of strings to pointers
//
if (EntriesRead > 0) {
DWORD i;
LPNETRESOURCEW NetR;
NetR = lpBuffer;
for (i = 0; i < EntriesRead; i++, NetR++) {
if (NetR->lpLocalName != NULL) {
NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpLocalName);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpRemoteName);
if (NetR->lpComment != NULL) {
NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpComment);
}
if (NetR->lpProvider != NULL) {
NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
(DWORD) NetR->lpProvider);
}
}
}
return status;
}
DWORD
APIENTRY
NPCloseEnum(
HANDLE hEnum
)
/*++
Routine Description:
This function closes the enumeration context handle.
Arguments:
hEnum - Supplies the enumeration context handle.
NOTE: If this value is 0xFFFFFFFF, it is not a context
handle. Just return success.
Return Value:
NO_ERROR - Successfully returned at least one entry.
WN_BAD_HANDLE - The enumeration handle is invalid.
--*/
{
DWORD status = NO_ERROR;
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWPROVAU: NPCloseEnum\n"));
}
#endif
RpcTryExcept
{
if (hEnum == (HANDLE) 0xFFFFFFFF) {
status = NO_ERROR;
}
else {
status = NwrCloseEnum(
(LPNWWKSTA_CONTEXT_HANDLE) &hEnum
);
}
}
RpcExcept(1) {
status = NwpMapRpcError(RpcExceptionCode());
}
RpcEndExcept
if (status != NO_ERROR) {
SetLastError(status);
}
return status;
}
DWORD
APIENTRY
NPFormatNetworkName(
LPWSTR lpRemoteName,
LPWSTR lpFormattedName,
LPDWORD lpnLength,
DWORD dwFlags,
DWORD dwAveCharPerLine
)
/*++
Routine Description:
This function takes a fully-qualified UNC name and formats it
into a shorter form for display. Only the name of the object
within the container is returned for display.
We only support formatting of the remote resource name to the
abbreviated form for display during enumeration where the container
name is displayed prior to the object within it.
Arguments:
lpRemoteName - Supplies the fully-qualified UNC name.
lpFormatedName - Output buffer to receive the formatted name.
lpnLength - On input, supplies the length of the lpFormattedName
buffer in characters. On output, if WN_MORE_DATA is returned,
receives the length in number of characters required of the
output buffer to hold the formatted name.
dwFlags - Supplies a bitwise set of flags indicating the type
of formatting required on lpRemoteName.
dwAveCharPerLine - Ignored.
Return Value:
NO_ERROR - Successfully returned at least one entry.
WN_MORE_DATA - lpFormattedName buffer is too small.
WN_BAD_VALUE - lpRemoteName is NULL.
ERROR_NOT_SUPPORTED - dwFlags that does not contain the
WNFMT_INENUM bit.
--*/
{
DWORD status = NO_ERROR;
LPWSTR NextBackSlash;
LPWSTR Source;
DWORD SourceLen;
#if DBG
IF_DEBUG(OTHER)
KdPrint(("\nNWPROVAU: NPFormatNetworkName\n"));
#endif
if (lpRemoteName == NULL)
{
status = WN_BAD_VALUE;
goto CleanExit;
}
if (dwFlags & WNFMT_INENUM)
{
BYTE i;
WORD length = wcslen( lpRemoteName );
WORD slashCount = 0;
WORD dotCount = 0;
WORD Start = 0;
WORD End = length;
BOOL isNdsUnc = FALSE;
for ( i = 0; i < length; i++ )
{
if ( lpRemoteName[i] == L'\\' )
{
slashCount++;
if ( i + 1 < length )
{
Start = i + 1;
}
}
if ( ( lpRemoteName[i] == L'.' ) ||
( lpRemoteName[i] == L'=' ) )
isNdsUnc = TRUE;
if ( dotCount < 1 && isNdsUnc && lpRemoteName[i] == L'.' )
{
End = i - 1;
dotCount++;
}
}
if ( i > length )
End = length - 1;
if ( slashCount > 3 || ( isNdsUnc != TRUE && slashCount != 3 && dotCount == 0 ) )
End = i - 1;
Source = &lpRemoteName[Start];
SourceLen = End - Start + 1;
if ( SourceLen + 1 > *lpnLength )
{
*lpnLength = SourceLen + 1;
status = WN_MORE_DATA;
}
else
{
wcsncpy( lpFormattedName, Source, SourceLen );
lpFormattedName[SourceLen] = 0x00000000;
status = NO_ERROR;
}
}
else if ( dwFlags & WNFMT_MULTILINE )
{
DWORD i, j, k = 0;
DWORD nLastBackSlash = 0;
DWORD BytesNeeded = ( wcslen( lpRemoteName ) + 1 +
2 * wcslen( lpRemoteName ) / dwAveCharPerLine
) * sizeof( WCHAR);
if ( *lpnLength < (BytesNeeded/sizeof(WCHAR)) )
{
*lpnLength = BytesNeeded/sizeof(WCHAR);
status = WN_MORE_DATA;
goto CleanExit;
}
for ( i = 0, j = 0; lpRemoteName[i] != 0; i++, j++ )
{
if ( lpRemoteName[i] == L'\\' )
nLastBackSlash = i;
if ( k == dwAveCharPerLine )
{
if ( lpRemoteName[i] != L'\\' )
{
DWORD m, n;
for ( n = nLastBackSlash, m = ++j ; n <= i ; n++, m-- )
{
lpFormattedName[m] = lpFormattedName[m-1];
}
lpFormattedName[m] = L'\n';
k = i - nLastBackSlash - 1;
}
else
{
lpFormattedName[j++] = L'\n';
k = 0;
}
}
lpFormattedName[j] = lpRemoteName[i];
k++;
}
lpFormattedName[j] = 0;
}
else if ( dwFlags & WNFMT_ABBREVIATED )
{
//
// we dont support abbreviated form for now because we look bad
// in comdlg (fileopen) if we do.
//
DWORD nLength;
nLength = wcslen( lpRemoteName ) + 1 ;
if (nLength > *lpnLength)
{
*lpnLength = nLength;
status = WN_MORE_DATA;
goto CleanExit;
}
else
{
wcscpy( lpFormattedName, lpRemoteName );
}
#if 0
DWORD i, j, k;
DWORD BytesNeeded = dwAveCharPerLine * sizeof( WCHAR);
DWORD nLength;
if ( *lpnLength < BytesNeeded )
{
*lpnLength = BytesNeeded;
status = WN_MORE_DATA;
goto CleanExit;
}
nLength = wcslen( lpRemoteName );
if ( ( nLength + 1) <= dwAveCharPerLine )
{
wcscpy( lpFormattedName, lpRemoteName );
}
else
{
lpFormattedName[0] = lpRemoteName[0];
lpFormattedName[1] = lpRemoteName[1];
for ( i = 2; lpRemoteName[i] != L'\\'; i++ )
lpFormattedName[i] = lpRemoteName[i];
for ( j = dwAveCharPerLine-1, k = nLength; j >= (i+3); j--, k-- )
{
lpFormattedName[j] = lpRemoteName[k];
if ( lpRemoteName[k] == L'\\' )
{
j--;
break;
}
}
lpFormattedName[j] = lpFormattedName[j-1] = lpFormattedName[j-2] = L'.';
for ( k = i; k < (j-2); k++ )
lpFormattedName[k] = lpRemoteName[k];
}
#endif
}
else // some unknown flags
{
status = ERROR_NOT_SUPPORTED;
}
CleanExit:
if (status != NO_ERROR)
SetLastError(status);
return status;
}
STATIC
BOOL
NwpWorkstationStarted(
VOID
)
/*++
Routine Description:
This function queries the service controller to see if the
NetWare workstation service has started. If in doubt, it returns
FALSE.
Arguments:
None.
Return Value:
Returns TRUE if the NetWare workstation service has started,
FALSE otherwise.
--*/
{
SC_HANDLE ScManager;
SC_HANDLE Service;
SERVICE_STATUS ServiceStatus;
BOOL IsStarted = FALSE;
ScManager = OpenSCManagerW(
NULL,
NULL,
SC_MANAGER_CONNECT
);
if (ScManager == NULL) {
return FALSE;
}
Service = OpenServiceW(
ScManager,
NW_WORKSTATION_SERVICE,
SERVICE_QUERY_STATUS
);
if (Service == NULL) {
CloseServiceHandle(ScManager);
return FALSE;
}
if (! QueryServiceStatus(Service, &ServiceStatus)) {
CloseServiceHandle(ScManager);
CloseServiceHandle(Service);
return FALSE;
}
if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
(ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
(ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
(ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
IsStarted = TRUE;
}
CloseServiceHandle(ScManager);
CloseServiceHandle(Service);
return IsStarted;
}
DWORD
NwpMapNameToUNC(
IN LPWSTR pszName,
OUT LPWSTR *ppszUNC
)
/*++
Routine Description:
This routine validates the given name as a netwarepath or UNC path.
If it is a netware path, this routine will convert the
Netware path name to UNC name.
Arguments:
pszName - Supplies the netware name or UNC name
ppszUNC - Points to the converted UNC name
Return Value:
NO_ERROR or the error that occurred.
--*/
{
DWORD err = NO_ERROR;
LPWSTR pszSrc = pszName;
LPWSTR pszDest;
BOOL fSlash = FALSE;
BOOL fColon = FALSE;
DWORD nServerLen = 0;
DWORD nVolLen = 0;
BOOL fFirstToken = TRUE;
*ppszUNC = NULL;
#if DBG
IF_DEBUG(CONNECT)
KdPrint(("NwpMapNameToUNC: Source = %ws\n", pszName ));
#endif
//
// Check if the given name is a valid UNC name
//
err = NwLibCanonRemoteName( NULL, // "\\Server" is valid UNC path
pszName,
ppszUNC,
NULL );
//
// The given name is a valid UNC name, so return success!
//
if ( err == NO_ERROR )
return err;
//
// The name cannot be NULL or empty string
//
if ( pszName == NULL || *pszName == 0)
return WN_BAD_NETNAME;
//
// Allocate the buffer to store the mapped UNC name
// We allocate 3 extra characters, two for the backslashes in front
// and one for the ease of parsing below.
//
if ((*ppszUNC = (LPVOID) LocalAlloc(
LMEM_ZEROINIT,
(wcslen( pszName) + 4) * sizeof( WCHAR)
)) == NULL )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy( *ppszUNC, L"\\\\" );
pszDest = *ppszUNC + 2; // Skip past two backslashes
//
// Parse the given string and put the converted string into *ppszUNC
// In the converted string, we will substitute 0 for all slashes
// for the time being.
//
for ( ; *pszSrc != 0; pszSrc++ )
{
if ( ( *pszSrc == L'/' )
|| ( *pszSrc == L'\\' )
)
{
//
// Two consecutive backslashes are bad
//
if ( (*(pszSrc+1) == L'/') || (*(pszSrc+1) == L'\\'))
{
LocalFree( *ppszUNC );
*ppszUNC = NULL;
return WN_BAD_NETNAME;
}
if ( !fSlash )
fSlash = TRUE;
*pszDest++ = 0;
}
else if ( (*pszSrc == L':') && fSlash && !fColon )
{
fColon = TRUE;
if ( *(pszSrc+1) != 0 )
*pszDest++ = 0;
}
else
{
*pszDest++ = *pszSrc;
if (( fSlash ) && ( !fColon))
nVolLen++;
else if ( !fSlash )
nServerLen++;
}
}
//
// Note: *ppszUNC is already terminated with two '\0' because we initialized
// the whole buffer to zero.
//
if ( ( nServerLen == 0 )
|| ( fSlash && nVolLen == 0 )
|| ( fSlash && nVolLen != 0 && !fColon )
)
{
LocalFree( *ppszUNC );
*ppszUNC = NULL;
return WN_BAD_NETNAME;
}
//
// At this point, we know the name is a valid Netware syntax
// i.e. SERVER[/VOL:/dir]
// We now need to validate that all the characters used in the
// servername, volume, directory are valid characters
//
pszDest = *ppszUNC + 2; // Skip past the first two backslashes
while ( *pszDest != 0 )
{
DWORD nLen = wcslen( pszDest );
if ( ( fFirstToken && !IS_VALID_SERVER_TOKEN( pszDest, nLen ))
|| ( !fFirstToken && !IS_VALID_TOKEN( pszDest, nLen ))
)
{
LocalFree( *ppszUNC );
*ppszUNC = NULL;
return WN_BAD_NETNAME;
}
fFirstToken = FALSE;
pszDest += nLen + 1;
}
//
// The netware name is valid! Convert 0 back to backslash in
// converted string.
//
pszDest = *ppszUNC + 2; // Skip past the first two backslashes
while ( *pszDest != 0 )
{
if ( (*(pszDest+1) == 0 ) && (*(pszDest+2) != 0 ) )
{
*(pszDest+1) = L'\\';
}
pszDest++;
}
#if DBG
IF_DEBUG(CONNECT)
KdPrint(("NwpMapNameToUNC: Destination = %ws\n", *ppszUNC ));
#endif
return NO_ERROR;
}
STATIC
VOID
NwpGetUncInfo(
IN LPWSTR lpstrUnc,
OUT WORD * slashCount,
OUT BOOL * isNdsUnc
)
{
BYTE i;
WORD length = wcslen( lpstrUnc );
*isNdsUnc = FALSE;
*slashCount = 0;
for ( i = 0; i < length; i++ )
{
if ( ( lpstrUnc[i] == L'.' ) && ( *slashCount == 3 ) )
{
*isNdsUnc = TRUE;
}
if ( lpstrUnc[i] == L'\\' )
{
*slashCount += 1;
}
}
}
STATIC
DWORD
NwpGetUncObjectName(
IN LPWSTR ContainerName
)
{
WORD length = 2;
WORD totalLength = wcslen( ContainerName );
if ( totalLength < 2 )
return 0;
while ( length < totalLength )
{
if ( ContainerName[length] == L'.' )
ContainerName[length] = L'\0';
length++;
}
length = 2;
while ( length < totalLength && ContainerName[length] != L'\\' )
{
length++;
}
if ( ( ContainerName[length + 1] == L'C' ||
ContainerName[length + 1] == L'c' ) &&
( ContainerName[length + 2] == L'N' ||
ContainerName[length + 2] == L'n' ) &&
ContainerName[length + 3] == L'=' )
{
ContainerName[length + 2] = L'\\';
ContainerName[length + 3] = L'\\';
return (DWORD) (ContainerName + length + 2);
}
ContainerName[length - 1] = L'\\';
return (DWORD) (ContainerName + length - 1);
}
STATIC
WORD
NwpGetSlashCount(
IN LPWSTR lpstrUnc
)
{
WORD count = 0;
BYTE i;
WORD length = wcslen( lpstrUnc );
for ( i = 0; i < length; i++ )
{
if ( lpstrUnc[i] == L'\\' )
{
count++;
}
}
return count;
}
DWORD
NwpMapRpcError(
IN DWORD RpcError
)
/*++
Routine Description:
This routine maps the RPC error into a more meaningful windows
error for the caller.
Arguments:
RpcError - Supplies the exception error raised by RPC
Return Value:
Returns the mapped error.
--*/
{
switch (RpcError) {
case RPC_S_UNKNOWN_IF:
case RPC_S_SERVER_UNAVAILABLE:
return WN_NO_NETWORK;
case RPC_S_INVALID_BINDING:
case RPC_X_SS_IN_NULL_CONTEXT:
case RPC_X_SS_CONTEXT_DAMAGED:
case RPC_X_SS_HANDLES_MISMATCH:
case ERROR_INVALID_HANDLE:
return ERROR_INVALID_HANDLE;
case RPC_X_NULL_REF_POINTER:
return ERROR_INVALID_PARAMETER;
case EXCEPTION_ACCESS_VIOLATION:
return ERROR_INVALID_ADDRESS;
default:
return RpcError;
}
}
DWORD
NwRegisterGatewayShare(
IN LPWSTR ShareName,
IN LPWSTR DriveName
)
/*++
Routine Description:
This routine remembers that a gateway share has been created so
that it can be cleanup up when NWCS is uninstalled.
Arguments:
ShareName - name of share
DriveName - name of drive that is shared
Return Status:
Win32 error of any failure.
--*/
{
return ( NwpRegisterGatewayShare(ShareName, DriveName) ) ;
}
DWORD
NwCleanupGatewayShares(
VOID
)
/*++
Routine Description:
This routine cleans up all persistent share info and also tidies
up the registry for NWCS. Later is not needed in uninstall, but is
there so we have a single routine that cvompletely disables the
gateway.
Arguments:
None.
Return Status:
Win32 error for failed APIs.
--*/
{
return ( NwpCleanupGatewayShares() ) ;
}
DWORD
NwClearGatewayShare(
IN LPWSTR ShareName
)
/*++
Routine Description:
This routine deletes a specific share from the remembered gateway
shares in the registry.
Arguments:
ShareName - share value to delete
Return Status:
Win32 status code.
--*/
{
return ( NwpClearGatewayShare( ShareName ) ) ;
}