diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/svcdlls/nwwks/client/provider.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/nw/svcdlls/nwwks/client/provider.c')
-rw-r--r-- | private/nw/svcdlls/nwwks/client/provider.c | 3051 |
1 files changed, 3051 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/client/provider.c b/private/nw/svcdlls/nwwks/client/provider.c new file mode 100644 index 000000000..4f8bf2c30 --- /dev/null +++ b/private/nw/svcdlls/nwwks/client/provider.c @@ -0,0 +1,3051 @@ +/*++ + +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 ) ) ; +} |