You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2994 lines
88 KiB
2994 lines
88 KiB
/*++
|
|
|
|
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
|
|
LPWSTR
|
|
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
|
|
//
|
|
LPWSTR lpNewBuffer;
|
|
|
|
if (!(lpNewBuffer = (LPWSTR) LocalReAlloc(
|
|
(HLOCAL) lpRemoteBuffer,
|
|
dwCharsRequired * sizeof(WCHAR),
|
|
LMEM_MOVEABLE)))
|
|
{
|
|
status = GetLastError() ;
|
|
LocalFree(lpRemoteBuffer);
|
|
lpRemoteBuffer = NULL;
|
|
goto ErrorExit ;
|
|
}
|
|
lpRemoteBuffer = lpNewBuffer;
|
|
|
|
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;
|
|
|
|
//
|
|
// Get rid of the <space> if a NDS tree name ...
|
|
//
|
|
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;
|
|
|
|
// 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_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpLocalName);
|
|
}
|
|
|
|
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpRemoteName);
|
|
|
|
if (NetR->lpComment != NULL) {
|
|
NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpComment);
|
|
}
|
|
|
|
if (NetR->lpProvider != NULL) {
|
|
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) 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_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpLocalName);
|
|
}
|
|
|
|
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpRemoteName);
|
|
|
|
if (NetR->lpComment != NULL)
|
|
{
|
|
NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpComment);
|
|
}
|
|
|
|
if (NetR->lpProvider != NULL)
|
|
{
|
|
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpProvider);
|
|
}
|
|
|
|
if (SystemOffset != 0)
|
|
{
|
|
*lplpSystem = (LPWSTR) ((DWORD_PTR) 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_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpLocalName);
|
|
}
|
|
|
|
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpRemoteName);
|
|
|
|
if (NetR->lpComment != NULL)
|
|
{
|
|
NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpComment);
|
|
}
|
|
|
|
if (NetR->lpProvider != NULL)
|
|
{
|
|
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) 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_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpLocalName);
|
|
}
|
|
|
|
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpRemoteName);
|
|
|
|
if (NetR->lpComment != NULL) {
|
|
NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) NetR->lpComment);
|
|
}
|
|
|
|
if (NetR->lpProvider != NULL) {
|
|
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
|
|
(DWORD_PTR) 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)
|
|
{
|
|
WORD i;
|
|
WORD length = (WORD) wcslen( lpRemoteName );
|
|
WORD slashCount = 0;
|
|
WORD dotCount = 0;
|
|
WORD Start = 0;
|
|
WORD End = length;
|
|
BOOL isNdsUnc = FALSE;
|
|
BOOL couldBeNdsUnc = FALSE;
|
|
|
|
if ( lpRemoteName[0] == L' ' )
|
|
couldBeNdsUnc = TRUE;
|
|
|
|
for ( i = 0; i < length; i++ )
|
|
{
|
|
if ( lpRemoteName[i] == L'\\' )
|
|
{
|
|
slashCount++;
|
|
if ( i + 1 < length )
|
|
{
|
|
Start = i + 1;
|
|
}
|
|
}
|
|
|
|
if ( couldBeNdsUnc &&
|
|
( ( 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;
|
|
|
|
//
|
|
// The name cannot be NULL or empty string
|
|
//
|
|
if ( pszName == NULL || *pszName == 0)
|
|
return WN_BAD_NETNAME;
|
|
|
|
#if DBG
|
|
IF_DEBUG(CONNECT)
|
|
KdPrint(("NwpMapNameToUNC: Source = %ws\n", pszName ));
|
|
#endif
|
|
|
|
//
|
|
// Get rid of the <space> if a NDS tree name ...
|
|
//
|
|
if ( pszName[0] == L' ' &&
|
|
pszName[1] == L'\\' &&
|
|
pszName[2] == L'\\' )
|
|
pszName = &pszName[1];
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// 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
|
|
)
|
|
{
|
|
WORD i;
|
|
WORD length = (WORD) 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
|
|
LPWSTR
|
|
NwpGetUncObjectName(
|
|
IN LPWSTR ContainerName
|
|
)
|
|
{
|
|
WORD length = 2;
|
|
WORD totalLength = (WORD) 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 (ContainerName + length + 2);
|
|
}
|
|
|
|
ContainerName[length - 1] = L'\\';
|
|
|
|
return (ContainerName + length - 1);
|
|
}
|
|
|
|
|
|
STATIC
|
|
WORD
|
|
NwpGetSlashCount(
|
|
IN LPWSTR lpstrUnc
|
|
)
|
|
{
|
|
WORD count = 0;
|
|
WORD i;
|
|
WORD length = (WORD) 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;
|
|
}
|
|
}
|