/*++ 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 #include #include #include #include #include #include #include #include #include // WNFMT_ manifests #include #ifndef NT1057 #include #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 // 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; // // Get rid of the 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= // 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) { BYTE 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 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 ) { BYTE 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; BYTE 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; } } 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 ) ) ; }