/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    provider.c

Abstract:

    This module contains NetWare Network Provider code.  It is the
    client-side wrapper for APIs supported by the Workstation service.

Author:

    Rita Wong  (ritaw)   15-Feb-1993

Revision History:

    Yi-Hsin Sung (yihsins) 10-July-1993
        Moved all dialog handling to nwdlg.c

--*/

#include <nwclient.h>
#include <nwsnames.h>
#include <nwcanon.h>
#include <validc.h>
#include <nwevent.h>
#include <ntmsv1_0.h>
#include <nwdlg.h>
#include <nwreg.h>
#include <nwauth.h>
#include <mpr.h>    // WNFMT_ manifests
#include <nwmisc.h>

#ifndef NT1057
#include <nwutil.h>
#endif

//-------------------------------------------------------------------//
//                                                                   //
// Local Function Prototypes                                         //
//                                                                   //
//-------------------------------------------------------------------//

STATIC
BOOL
NwpWorkstationStarted(
    VOID
    );

STATIC
DWORD
NwpMapNameToUNC(
    IN LPWSTR pszName,
    OUT LPWSTR *ppszUNC
    );

STATIC
VOID
NwpGetUncInfo(
    IN LPWSTR lpstrUnc,
    OUT WORD * slashCount,
    OUT BOOL * isNdsUnc
    );

STATIC
LPWSTR
NwpGetUncObjectName(
    IN LPWSTR ContainerName
    );

//-------------------------------------------------------------------//
//                                                                   //
// Global variables                                                  //
//                                                                   //
//-------------------------------------------------------------------//

#if DBG
DWORD NwProviderTrace = 0;
#endif


DWORD
APIENTRY
NPGetCaps(
    IN DWORD QueryVal
    )
/*++

Routine Description:

    This function returns the functionality supported by this network
    provider.

Arguments:

    QueryVal - Supplies a value which determines the type of information
        queried regarding the network provider's support in this area.

Return Value:

    Returns a value which indicates the level of support given by this
    provider.

--*/
{

#if DBG
    IF_DEBUG(INIT) {
        KdPrint(("\nNWPROVAU: NPGetCaps %lu\n", QueryVal));
    }
#endif

    switch (QueryVal) {

        case WNNC_SPEC_VERSION:
            return 0x00040000;

        case WNNC_NET_TYPE:
            return WNNC_NET_NETWARE ; 

        case WNNC_USER:
            return WNNC_USR_GETUSER;

        case WNNC_CONNECTION:
            return (WNNC_CON_ADDCONNECTION |
                    WNNC_CON_ADDCONNECTION3 |
                    WNNC_CON_CANCELCONNECTION |
                    WNNC_CON_GETPERFORMANCE |
                    WNNC_CON_GETCONNECTIONS);

        case WNNC_ENUMERATION:
            return ( WNNC_ENUM_GLOBAL |
                     WNNC_ENUM_CONTEXT |
                     WNNC_ENUM_LOCAL );

        case WNNC_START:
            if (NwpWorkstationStarted()) {
                return 1;
            }
            else {
                return 0xffffffff;   // don't know
            }

        case WNNC_DIALOG:
            return WNNC_DLG_FORMATNETWORKNAME
#ifdef NT1057
                 ;
#else
                 | WNNC_DLG_GETRESOURCEPARENT | WNNC_DLG_GETRESOURCEINFORMATION;
#endif

        //
        // The rest are not supported by the NetWare provider
        //
        default:
            return 0;
    }

}

#define NW_EVENT_MESSAGE_FILE          L"nwevent.dll"



DWORD
APIENTRY
NPGetUser(
    LPWSTR  lpName,
    LPWSTR  lpUserName,
    LPDWORD lpUserNameLen
    )
/*++

Routine Description:

    This is used to determine either the current default username, or the
    username used to establish a network connection.

Arguments:

    lpName - Contains the name of the local device the caller is interested
        in, or a network name that the user has made a connection to. This
        may be NULL or the empty string if the caller is interested in the
        name of the user currently logged on to the system. If a network
        name is passed in, and the user is connected to that resource using
        different names, it is possible that a provider cannont resolve 
        which username to return. In this case the provider may make an
        arbitrary choice amonst the possible usernames.

    lpUserName - Points to a buffer to receive the user name. this should
        be a name that can be passed into the NPAddConnection or
        NPAddConnection3 function to re-establish the connection with the
        same user name.

    lpBufferSize - This is used to specify the size (in characters) of the
        buffer passed in. If the call fails because the buffer is not big
        enough, this location will be used to return the required buffer size.

Return Value:

    WN_SUCCESS - If the call is successful. Otherwise, an error code is,
        returned, which may include:

    WN_NOT_CONNECTED - lpName not a redirected device nor a connected network
        name.

    WN_MORE_DATA - The buffer is too small.

    WN_NO_NETWORK - Network not present.

--*/
{
    DWORD  status;
    DWORD  dwUserNameBufferSize = *lpUserNameLen * sizeof(WCHAR);
    DWORD  CharsRequired = 0;

    if (lpName == NULL)
    {
        return WN_NOT_CONNECTED;
    }

    RtlZeroMemory( lpUserName, dwUserNameBufferSize );

#if DBG
    IF_DEBUG(CONNECT)
    {
        KdPrint(("\nNWPROVAU: NPGetUser %ws\n", lpName));
    }
#endif

    RpcTryExcept
    {
            status = NwrGetUser(
                        NULL,
                        lpName,
                        (LPBYTE) lpUserName,
                        dwUserNameBufferSize,
                        &CharsRequired
                        );

            if (status == WN_MORE_DATA)
            {
                //
                // Output buffer too small.
                //
                *lpUserNameLen = CharsRequired;
            }
    }
    RpcExcept(1)
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {
        SetLastError(status);
    }

    return status;
}


DWORD
APIENTRY
NPAddConnection(
    LPNETRESOURCEW lpNetResource,
    LPWSTR lpPassword,
    LPWSTR lpUserName
    )
/*++

Routine Description:

    This function creates a remote connection.

Arguments:

    lpNetResource - Supplies the NETRESOURCE structure which specifies
        the local DOS device to map, the remote resource to connect to
        and other attributes related to the connection.

    lpPassword - Supplies the password to connect with.

    lpUserName - Supplies the username to connect with.

Return Value:

    NO_ERROR - Successful.

    WN_BAD_VALUE - Invalid value specifed in lpNetResource.

    WN_BAD_NETNAME - Invalid remote resource name.

    WN_BAD_LOCALNAME - Invalid local DOS device name.

    WN_BAD_PASSWORD - Invalid password.

    WN_ALREADY_CONNECTED - Local DOS device name is already in use.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;
    LPWSTR pszRemoteName = NULL;

    UCHAR EncodeSeed = NW_ENCODE_SEED3;
    UNICODE_STRING PasswordStr;

    LPWSTR CachedUserName = NULL ;
    LPWSTR CachedPassword = NULL ;

    PasswordStr.Length = 0;

    status = NwpMapNameToUNC(
                 lpNetResource->lpRemoteName,
                 &pszRemoteName );

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return status;
    }

#if DBG
    IF_DEBUG(CONNECT) {
        KdPrint(("\nNWPROVAU: NPAddConnection %ws\n", pszRemoteName));
    }
#endif

    RpcTryExcept
    {
        if (lpNetResource->dwType != RESOURCETYPE_ANY &&
            lpNetResource->dwType != RESOURCETYPE_DISK &&
            lpNetResource->dwType != RESOURCETYPE_PRINT)
        {
            status = WN_BAD_VALUE;
        }
        else
        {
#ifdef NT1057
            //
            // no credentials specified, see if we have cached credentials
            //
            if (!lpPassword && !lpUserName) 
            {
                 (void) NwpRetrieveCachedCredentials(
                            pszRemoteName,
                            &CachedUserName,
                            &CachedPassword) ;

                 //
                 // these values will be NULL still if nothing found
                 //
                 lpPassword = CachedPassword ;
                 lpUserName = CachedUserName ;
            }
#endif

            //
            // Encode password.
            //
            RtlInitUnicodeString(&PasswordStr, lpPassword);
            RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);

            status = NwrCreateConnection(
                        NULL,
                        lpNetResource->lpLocalName,
                        pszRemoteName,
                        lpNetResource->dwType,
                        lpPassword,
                        lpUserName
                        );

            if (CachedUserName)
            {
                (void)LocalFree((HLOCAL)CachedUserName);
            }

            if (CachedPassword)
            {
                RtlZeroMemory(CachedPassword,
                              wcslen(CachedPassword) *
                              sizeof(WCHAR)) ;
                (void)LocalFree((HLOCAL)CachedPassword);
            }
        }
    }
    RpcExcept(1)
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (PasswordStr.Length != 0 && !CachedPassword)
    {
        //
        // Restore password to original state
        //
        RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &PasswordStr);
    }

    if (status == ERROR_SHARING_PAUSED)
    {
        HMODULE MessageDll;
        WCHAR Buffer[1024];
        DWORD MessageLength;
        DWORD err;
        HKEY  hkey;
        LPWSTR pszProviderName = NULL;
    
        //
        // Load the netware message file DLL
        //
        MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
    
        if (MessageDll == NULL)
        {
            goto ExitPoint ;
        }

        //
        // Read the Network Provider Name.
        //
        // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
        // \NWCWorkstation\networkprovider
        //
        err = RegOpenKeyExW(
                  HKEY_LOCAL_MACHINE,
                  NW_WORKSTATION_PROVIDER_PATH,
                  REG_OPTION_NON_VOLATILE,   // options
                  KEY_READ,                  // desired access
                  &hkey
                  );
    
        if ( !err )
        {
            //
            // ignore the return code. if fail, pszProviderName is NULL
            //
            err =  NwReadRegValue(
                      hkey,
                      NW_PROVIDER_VALUENAME,
                      &pszProviderName          // free with LocalFree
                      );
    
            RegCloseKey( hkey );
        }

        if (err)
        {
            (void) FreeLibrary(MessageDll);
            goto ExitPoint ;
        }

        RtlZeroMemory(Buffer, sizeof(Buffer)) ;

        //
        // Get string from message file 
        //
        MessageLength = FormatMessageW(
                            FORMAT_MESSAGE_FROM_HMODULE,
                            (LPVOID) MessageDll,
                            NW_LOGIN_DISABLED,
                            0,
                            Buffer,
                            sizeof(Buffer) / sizeof(WCHAR),
                            NULL
                            );

        if (MessageLength != 0)
        {
            status = WN_EXTENDED_ERROR ;
            WNetSetLastErrorW(NW_LOGIN_DISABLED, 
                              Buffer,
                              pszProviderName) ;
        }

        (void) LocalFree( (HLOCAL) pszProviderName );
        (void) FreeLibrary(MessageDll);

    }

ExitPoint: 

    if (status != NO_ERROR)
    {
        SetLastError(status);
    }
    
    LocalFree( (HLOCAL) pszRemoteName );
    return status;
}


DWORD
APIENTRY
NPAddConnection3(
    HWND   hwndOwner,
    LPNETRESOURCEW lpNetResource,
    LPWSTR lpPassword,
    LPWSTR lpUserName,
    DWORD  dwConnFlags
    )
/*++

Routine Description:

    This function creates a remote connection.

Arguments:

    hwndOwner - Owner window handle for dialog boxes

    lpNetResource - Supplies the NETRESOURCE structure which specifies
        the local DOS device to map, the remote resource to connect to
        and other attributes related to the connection.

    lpPassword - Supplies the password to connect with.

    lpUserName - Supplies the username to connect with.

    dwConnFlags -  CONNECT_UPDATE_PROFILE...

Return Value:

    NO_ERROR - Successful.

    WN_BAD_VALUE - Invalid value specifed in lpNetResource.

    WN_BAD_NETNAME - Invalid remote resource name.

    WN_BAD_LOCALNAME - Invalid local DOS device name.

    WN_BAD_PASSWORD - Invalid password.

    WN_ALREADY_CONNECTED - Local DOS device name is already in use.

    Other network errors.

--*/
{
    DWORD err = NO_ERROR;
    LPWSTR UserName = NULL;
    LPWSTR Password = NULL;

    if (   ( dwConnFlags & CONNECT_PROMPT )
       && !( dwConnFlags & CONNECT_INTERACTIVE )
       )
    {
        return WN_BAD_VALUE;
    }

    if ( !(dwConnFlags & CONNECT_PROMPT ))
    {
        err = NPAddConnection( lpNetResource,
                               lpPassword, 
                               lpUserName );

        if (  ( err == NO_ERROR ) 
           || !( dwConnFlags & CONNECT_INTERACTIVE )  // Cannot popup dialog
           )
        {
            return err;
        }
    }

    for (;;)
    {
        if (  ( err != NO_ERROR )             // CONNECT_PROMPT
           && ( err != WN_BAD_PASSWORD )
           && ( err != WN_ACCESS_DENIED )
           && ( err != ERROR_NO_SUCH_USER )
           )
        {
            // Errors not related to access problems
            break;
        }

        if ( UserName )
        {
            (void) LocalFree( UserName );
            UserName = NULL;
        }
 
        if ( Password )
        {
            memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
            (void) LocalFree( Password );
            Password = NULL;
        }

        //
        // Put up dialog to get username
        // and password.
        //
        err = NwpGetUserCredential( hwndOwner,
                                    lpNetResource->lpRemoteName,
                                    err,
                                    lpUserName,
                                    &UserName,
                                    &Password );

        if ( err != NO_ERROR )
            break;

        err = NPAddConnection( lpNetResource,
                               Password, 
                               UserName );

        if ( err == NO_ERROR )
        {
#if 0
            if ( (UserName != NULL) && (Password != NULL))
            {
                // Checking UserName and Password is to make sure that
                // we have prompted for password
                (VOID) NwpCacheCredentials( lpNetResource->lpRemoteName,
                                            UserName,
                                            Password ) ;
            }
#endif
            break;
        }
    }

    if ( UserName )
        (void) LocalFree( UserName );
 
    if ( Password )
    {
        memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
        (void) LocalFree( Password );
    }

    return err;
}



DWORD
APIENTRY
NPCancelConnection(
    LPWSTR lpName,
    BOOL fForce
    )
/*++

Routine Description:

    This function deletes a remote connection.

Arguments:

    lpName - Supplies the local DOS device, or the remote resource name
        if it is a UNC connection to delete.

    fForce - Supplies the force level to break the connection.  TRUE means
        to forcefully delete the connection, FALSE means end the connection
        only if there are no opened files.

Return Value:

    NO_ERROR - Successful.

    WN_BAD_NETNAME - Invalid remote resource name.

    WN_NOT_CONNECTED - Connection could not be found.

    WN_OPEN_FILES - fForce is FALSE and there are opened files on the
        connection.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;
    LPWSTR pszName = NULL;

    // 
    // We only need to map remote resource name  
    //

    if ( NwLibValidateLocalName( lpName ) != NO_ERROR )
    {
        status = NwpMapNameToUNC(
                     lpName,
                     &pszName 
                     );

        if (status != NO_ERROR) {
            SetLastError(status);
            return status;
        }
    }

#if DBG
    IF_DEBUG(CONNECT) {
        KdPrint(("\nNWPROVAU: NPCancelConnection %ws, Force %u\n",
                 pszName? pszName : lpName, fForce));
    }
#endif

    RpcTryExcept {

        status = NwrDeleteConnection(
                    NULL,
                    pszName? pszName : lpName,
                    (DWORD) fForce
                    );

    }
    RpcExcept(1) {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
    }

    LocalFree( (HLOCAL) pszName );
    return status;

}



DWORD
APIENTRY
NPGetConnection(
    LPWSTR lpLocalName,
    LPWSTR lpRemoteName,
    LPDWORD lpnBufferLen
    )
/*++

Routine Description:

    This function returns the remote resource name for a given local
    DOS device.

Arguments:

    lpLocalName - Supplies the local DOS device to look up.

    lpRemoteName - Output buffer to receive the remote resource name
        mapped to lpLocalName.

    lpnBufferLen - On input, supplies length of the lpRemoteName buffer
        in number of characters.  On output, if error returned is
        WN_MORE_DATA, receives the number of characters required of
        the output buffer to hold the output string.

Return Value:

    NO_ERROR - Successful.

    WN_BAD_LOCALNAME - Invalid local DOS device.

    WN_NOT_CONNECTED - Connection could not be found.

    WN_MORE_DATA - Output buffer is too small.

    Other network errors.

--*/
{

    DWORD status = NO_ERROR;
    DWORD CharsRequired;

#if DBG
    IF_DEBUG(CONNECT) {
        KdPrint(("\nNWPROVAU: NPGetConnection %ws\n", lpLocalName));
    }
#endif

    RpcTryExcept {

        if (lpRemoteName && *lpnBufferLen)
            *lpRemoteName = 0 ;

        status = NwrQueryServerResource(
                    NULL,
                    lpLocalName,
                    (*lpnBufferLen == 0? NULL : lpRemoteName),
                    *lpnBufferLen,
                    &CharsRequired
                    );
         
         if (status == ERROR_INSUFFICIENT_BUFFER)
             status = WN_MORE_DATA;

        if (status == WN_MORE_DATA) {
            *lpnBufferLen = CharsRequired;
        }

    }
    RpcExcept(1) {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
    }

#if DBG
    IF_DEBUG(CONNECT) {
        KdPrint(("\nNWPROVAU: NPGetConnection returns %lu\n", status));
        if (status == NO_ERROR) {
            KdPrint(("                                  %ws, BufferLen %lu, CharsRequired %lu\n", lpRemoteName, *lpnBufferLen, CharsRequired));

        }
    }
#endif

    return status;
}


DWORD
APIENTRY
NPGetConnectionPerformance(
    LPCWSTR lpRemoteName,
    LPNETCONNECTINFOSTRUCT lpNetConnectInfo
    )
/*++

Routine Description:

    This function returns information about the expected performance of a
    connection used to access a network resource. The request can only be
    for a network resource to which there is currently a connection.

Arguments:

    lpRemoteName - Contains the local name or remote name for a resource
                   for which a connection exists.

    lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
                       which is to be filled if the connection performance
                       of connection lpRemoteName can be determined.

Return Value:

    NO_ERROR - Successful.

    WN_NOT_CONNECTED - Connection could not be found.

    WN_NONETWORK - Network is not present.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;
    LPWSTR pszRemoteName;

    if ( lpNetConnectInfo == NULL )
    {
            status = ERROR_INVALID_PARAMETER;
            SetLastError(status);
            return status;
    }

    pszRemoteName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
                                         ( wcslen(lpRemoteName) + 1 ) *
                                         sizeof(WCHAR) );

    if ( pszRemoteName == NULL )
    {
            status = ERROR_NOT_ENOUGH_MEMORY;
            SetLastError(status);
            return status;
    }

    wcscpy( pszRemoteName, lpRemoteName );
    _wcsupr( pszRemoteName );

#if DBG
    IF_DEBUG(CONNECT) {
        KdPrint(("\nNWPROVAU: NPGetConnectionPerformance %ws\n", pszRemoteName));
    }
#endif

    RpcTryExcept {

        status = NwrGetConnectionPerformance(
                    NULL,
                    pszRemoteName,
                    (LPBYTE) lpNetConnectInfo,
                    sizeof(NETCONNECTINFOSTRUCT) );

    }
    RpcExcept(1)
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {
        SetLastError(status);
    }

    LocalFree( (HLOCAL) pszRemoteName );
    return status;
}



DWORD
APIENTRY
NPGetUniversalName(
#ifdef NT1057
    LPWSTR  lpLocalPath,
#else
    LPCWSTR lpLocalPath,
#endif
    DWORD  dwInfoLevel,
    LPVOID lpBuffer,
    LPDWORD lpBufferSize
    )
/*++

Routine Description:

    This function returns the universal resource name for a given local
    path.

Arguments:

    lpLocalPath - Supplies the local DOS Path to look up.

    dwInfoLevel - Info level requested.

    lpBuffer - Output buffer to receive the appropruatye structure.

    lpBufferLen - On input, supplies length of the buffer in number of 
        bytes.  On output, if error returned is WN_MORE_DATA, receives 
        the number of bytes required of the output buffer.

Return Value:

    NO_ERROR - Successful.

    WN_BAD_LOCALNAME - Invalid local DOS device.

    WN_NOT_CONNECTED - Connection could not be found.

    WN_MORE_DATA - Output buffer is too small.

    Other network errors.

--*/
{

    DWORD status = NO_ERROR;
    DWORD dwCharsRequired = MAX_PATH + 1 ;
    DWORD dwBytesNeeded ;
    DWORD dwLocalLength ;
    LPWSTR lpRemoteBuffer ;
    WCHAR  szDrive[3] ;

    //
    // check for bad info level
    //
    if ((dwInfoLevel != UNIVERSAL_NAME_INFO_LEVEL) &&
        (dwInfoLevel != REMOTE_NAME_INFO_LEVEL))
    {
        return WN_BAD_VALUE ;
    }

    //
    // check for bad pointers
    //
    if (!lpLocalPath || !lpBuffer || !lpBufferSize)
    {
        return WN_BAD_POINTER ;
    }
 
    //
    // local path must at least have "X:"
    //
    if (((dwLocalLength = wcslen(lpLocalPath)) < 2) ||
        (lpLocalPath[1] != L':') ||
        ((dwLocalLength > 2) && (lpLocalPath[2] != L'\\')))
    {
        return WN_BAD_VALUE ;
    }

    //
    // preallocate some memory
    //
    if (!(lpRemoteBuffer = (LPWSTR) LocalAlloc(
                                        LPTR, 
                                        dwCharsRequired * sizeof(WCHAR))))
    {
        status = GetLastError() ;
        goto ErrorExit ;
    }
    
    szDrive[2] = 0 ;
    wcsncpy(szDrive, lpLocalPath, 2) ;

    //
    // get the remote path by calling the existing API
    //
    status = NPGetConnection(
                 szDrive,
                 lpRemoteBuffer, 
                 &dwCharsRequired) ;

    if (status == WN_MORE_DATA)
    {
        //
        // reallocate the correct size
        //
        LPWSTR lpNewBuffer; 

        if (!(lpNewBuffer = (LPWSTR) LocalReAlloc(
                                            (HLOCAL) lpRemoteBuffer, 
                                            dwCharsRequired * sizeof(WCHAR),
                                            LMEM_MOVEABLE)))
        {
            status = GetLastError() ;
            LocalFree(lpRemoteBuffer);
            lpRemoteBuffer = NULL;
            goto ErrorExit ;
        }
        lpRemoteBuffer = lpNewBuffer;

        status = NPGetConnection(
                     szDrive,
                     lpRemoteBuffer, 
                     &dwCharsRequired) ;
    }

    if (status != WN_SUCCESS)
    {
        goto ErrorExit ;
    }
    
    //
    // at minimum we will need this size of the UNC name
    // the -2 is because we loose the drive letter & colon.
    //
    dwBytesNeeded = (wcslen(lpRemoteBuffer) +
                     dwLocalLength - 2 + 1) * sizeof(WCHAR) ;

    switch (dwInfoLevel)
    {
        case UNIVERSAL_NAME_INFO_LEVEL:
        {
            LPUNIVERSAL_NAME_INFO lpUniversalNameInfo ;

            //
            // calculate how many bytes we really need
            //
            dwBytesNeeded += sizeof(UNIVERSAL_NAME_INFO) ;

            if (*lpBufferSize < dwBytesNeeded)
            {
                *lpBufferSize = dwBytesNeeded ;
                status = WN_MORE_DATA ;
                break ;
            }
 
            //
            // now we are all set. just stick the data in the buffer
            //
            lpUniversalNameInfo = (LPUNIVERSAL_NAME_INFO) lpBuffer ;

            lpUniversalNameInfo->lpUniversalName = (LPWSTR)
                (((LPBYTE)lpBuffer) + sizeof(UNIVERSAL_NAME_INFO)) ;
            wcscpy(lpUniversalNameInfo->lpUniversalName,
                   lpRemoteBuffer) ;
            wcscat(lpUniversalNameInfo->lpUniversalName,
                   lpLocalPath+2) ;

            break ;
        }

        case REMOTE_NAME_INFO_LEVEL :
        {
            LPREMOTE_NAME_INFO lpRemoteNameInfo ;

            //
            // calculate how many bytes we really need
            //
            dwBytesNeeded *= 2 ;  // essentially twice the info + terminator 
            dwBytesNeeded += (sizeof(REMOTE_NAME_INFO) + sizeof(WCHAR)) ;

            if (*lpBufferSize < dwBytesNeeded)
            {
                *lpBufferSize = dwBytesNeeded ;
                status = WN_MORE_DATA ;
                break ;
            }

            //
            // now we are all set. just stick the data in the buffer
            //
            lpRemoteNameInfo = (LPREMOTE_NAME_INFO) lpBuffer ;

            lpRemoteNameInfo->lpUniversalName = (LPWSTR)
                (((LPBYTE)lpBuffer) + sizeof(REMOTE_NAME_INFO)) ;
            wcscpy(lpRemoteNameInfo->lpUniversalName,
                   lpRemoteBuffer) ;
            wcscat(lpRemoteNameInfo->lpUniversalName,
                   lpLocalPath+2) ;

            lpRemoteNameInfo->lpConnectionName = 
                lpRemoteNameInfo->lpUniversalName + 
                wcslen(lpRemoteNameInfo->lpUniversalName) + 1 ;
            wcscpy(lpRemoteNameInfo->lpConnectionName,
                   lpRemoteBuffer) ;

            lpRemoteNameInfo->lpRemainingPath = 
                lpRemoteNameInfo->lpConnectionName + 
                wcslen(lpRemoteNameInfo->lpConnectionName) + 1 ;
            wcscpy(lpRemoteNameInfo->lpRemainingPath,
                   lpLocalPath+2) ;

            break ;
        }

        default:
            //
            // yikes!
            //
            status = WN_BAD_VALUE ;
            ASSERT(FALSE);
    }

ErrorExit: 

    if (lpRemoteBuffer)
    {
        (void) LocalFree((HLOCAL)lpRemoteBuffer) ;
    }
    return status;
}



DWORD
APIENTRY
NPOpenEnum(
    DWORD dwScope,
    DWORD dwType,
    DWORD dwUsage,
    LPNETRESOURCEW lpNetResource,
    LPHANDLE lphEnum
    )
/*++

Routine Description:

    This function initiates an enumeration of either connections, or
    browsing of network resource.

Arguments:

    dwScope - Supplies the category of enumeration to do--either
        connection or network browsing.

    dwType - Supplies the type of resource to get--either disk,
        print, or it does not matter.

    dwUsage - Supplies the object type to get--either container,
        or connectable usage.

    lpNetResource - Supplies, in the lpRemoteName field, the container
        name to enumerate under.

    lphEnum - Receives the resumable context handle to be used on all
        subsequent calls to get the list of objects under the container.

Return Value:

    NO_ERROR - Successful.

    WN_BAD_VALUE - Either the dwScope, dwType, or the dwUsage specified
        is not acceptable.

    WN_BAD_NETNAME - Invalid remote resource name.

    WN_NOT_CONTAINER - Remote resource name is not a container.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;

#if DBG
    IF_DEBUG(ENUM)
    {
        KdPrint(("\nNWPROVAU: NPOpenEnum\n"));
    }
#endif


    RpcTryExcept
    {
        if ( ( dwType & RESOURCETYPE_DISK ) ||
             ( dwType & RESOURCETYPE_PRINT ) ||
             ( dwType == RESOURCETYPE_ANY ) )
        {
            switch ( dwScope )
            {
                case RESOURCE_CONNECTED:

                    status = NwrOpenEnumConnections( NULL,
                                                     dwType,
                                                     (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                    break;

                case RESOURCE_CONTEXT:

                    status = NwrOpenEnumContextInfo( NULL,
                                                     dwType,
                                                     (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                    break;

                case RESOURCE_GLOBALNET:

                    if ( lpNetResource == NULL )
                    {
                        //
                        // Enumerating servers and NDS trees
                        //
                        if ( dwUsage & RESOURCEUSAGE_CONTAINER || dwUsage == 0 )
                        {
                            status = NwrOpenEnumServersAndNdsTrees( NULL,
                                                         (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                        }
                        else
                        {
                            //
                            // There is no such thing as a connectable server
                            // object.
                            //
                            status = WN_BAD_VALUE;
                        }
                    }
                    else
                    {
                        BOOL IsEnumVolumes = TRUE;
                        LPWSTR pszRemoteName = NULL;
                        WORD slashCount;
                        BOOL isNdsUnc;

                        NwpGetUncInfo( lpNetResource->lpRemoteName,
                                       &slashCount,
                                       &isNdsUnc );

                        //
                        // Either enumerating volumes, directories, or NDS subtrees
                        //

                        if ( dwUsage & RESOURCEUSAGE_CONNECTABLE ||
                             dwUsage & RESOURCEUSAGE_CONTAINER ||
                             dwUsage == 0 )
                        {
                            LPWSTR tempStrPtr = lpNetResource->lpRemoteName;
                            DWORD dwClassType = 0;

                            //
                            // Get rid of the <space> if a NDS tree name ...
                            //
                            if ( tempStrPtr[0] == L' ' &&
                                 tempStrPtr[1] == L'\\' &&
                                 tempStrPtr[2] == L'\\' )
                                tempStrPtr = &tempStrPtr[1];

                            if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
                            {
                                if ( ( dwType == RESOURCETYPE_ANY ) ||
                                     ( ( dwType & RESOURCETYPE_DISK ) &&
                                       ( dwType & RESOURCETYPE_PRINT ) ) )
                                { 
                                    status = NwrOpenEnumNdsSubTrees_Any( NULL,
                                                                         tempStrPtr,
                                                                         NULL,
                                                              (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                }
                                else if ( dwType & RESOURCETYPE_DISK ) 
                                {
                                    status = NwrOpenEnumNdsSubTrees_Disk( NULL,
                                                                          tempStrPtr,
                                                                          NULL,
                                                              (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                }
                                else if ( dwType & RESOURCETYPE_PRINT )
                                { 
                                    status = NwrOpenEnumNdsSubTrees_Print( NULL,
                                                                           tempStrPtr,
                                                                           NULL,
                                                              (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                }
                                else
                                {
                                    KdPrint(("NWOpenEnum: Unhandled dwType %lu\n", dwType));
                                }
                            }
                            else if (
                                      ( slashCount < 4 ) &&
                                      ( ( dwType == RESOURCETYPE_ANY ) ||
                                        ( ( dwType & RESOURCETYPE_DISK ) &&
                                          ( dwType & RESOURCETYPE_PRINT ) ) ) &&
                                      ( ( status = NwrOpenEnumNdsSubTrees_Any( NULL,
                                                                               tempStrPtr,
                                                                               &dwClassType,
                                                            (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
                                        ==NO_ERROR )
                                    )
                            {
                                status = NO_ERROR;
                            }
                            else if (
                                      ( slashCount < 4 ) &&
                                      ( dwType & RESOURCETYPE_DISK ) &&
                                      ( ( status = NwrOpenEnumNdsSubTrees_Disk( NULL,
                                                                                tempStrPtr,
                                                                                &dwClassType,
                                                            (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
                                        ==NO_ERROR )
                                    )
                            {
                                status = NO_ERROR;
                            }
                            else if (
                                      ( slashCount < 4 ) &&
                                      ( dwType & RESOURCETYPE_PRINT ) &&
                                      ( ( status = NwrOpenEnumNdsSubTrees_Print( NULL,
                                                                                 tempStrPtr,
                                                                                 &dwClassType,
                                                            (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
                                        ==NO_ERROR )
                                    )
                            {
                                status = NO_ERROR;
                            }
                            else if (
                                     (slashCount < 4
                                      &&
                                      (status == ERROR_NETWORK_ACCESS_DENIED
                                       ||
                                       status == ERROR_GEN_FAILURE
                                       ||
                                       status == ERROR_ACCESS_DENIED
                                       ||
                                       status == ERROR_BAD_NETPATH
                                       ||
                                       status == WN_BAD_NETNAME
                                       ||
                                       status == ERROR_INVALID_NAME))
                                     ||
                                     ( slashCount > 3 && status == NO_ERROR )
                                    )
                            {
                                if (( status == ERROR_NETWORK_ACCESS_DENIED ) &&
                                    ( dwClassType == CLASS_TYPE_NCP_SERVER ))
                                {
                                    status = NO_ERROR;
                                    isNdsUnc = TRUE;
                                    IsEnumVolumes = TRUE;
                                }
                                else if ( ( status == ERROR_NETWORK_ACCESS_DENIED ) &&
                                          ( ( dwClassType == CLASS_TYPE_VOLUME ) ||
                                            ( dwClassType == CLASS_TYPE_DIRECTORY_MAP ) ) )
                                {
                                    status = NO_ERROR;
                                    isNdsUnc = TRUE;
                                    IsEnumVolumes = FALSE;
                                }
                                else
                                {
                                    //
                                    // A third backslash means that we want to
                                    // enumerate the directories.
                                    //

                                    if ( isNdsUnc && slashCount > 3 )
                                        IsEnumVolumes = FALSE;

                                    if ( !isNdsUnc && slashCount > 2 )
                                        IsEnumVolumes = FALSE;

                                    if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE )
                                        IsEnumVolumes = FALSE;
                                }

                                status = NwpMapNameToUNC( tempStrPtr,
                                                          &pszRemoteName );
 
                                if ( status == NO_ERROR )
                                {
                                    if ( IsEnumVolumes ) 
                                    {
                                        LPWSTR pszServerName = pszRemoteName;

                                        // The following 10 lines are a hack to 
                                        // allow the provider to browse past the CN=<server>
                                        // object in an NDS tree.
                                        if ( slashCount == 3 && isNdsUnc == TRUE )
                                        {
                                            pszServerName = (LPWSTR)
                                                          NwpGetUncObjectName( pszRemoteName );

                                            if ( pszServerName == NULL )
                                                pszServerName = pszRemoteName;
                                        }
                                        else if ( dwUsage & RESOURCEUSAGE_ATTACHED )
                                        {
#ifndef NT1057
                                            // This is a bindery server.
                                            // Return WN_NOT_AUTHENTICATED if
                                            // we are not already attached so
                                            // that clients ( explorer ) will
                                            // do NPAddConnection3 to make
                                            // a connection to the server.
                                            BOOL  fAttached;
                                            BOOL  fAuthenticated;

                                            status = NwIsServerOrTreeAttached(
                                                         pszServerName + 2,
                                                         &fAttached,
                                                         &fAuthenticated );

                                            if ( status != NO_ERROR )
                                                break;

                                            if ( !fAttached || !fAuthenticated)
                                            {
                                                // See if the server belongs to
                                                // our provider.
                                                status = NwrOpenEnumVolumes(
                                                             NULL,
                                                             pszServerName,
                                                             (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );

                                                if ( status == NO_ERROR )
                                                {
                                                    // The server belongs to us.
                                                    // Close the handle and
                                                    // return not attached if
                                                    // callee passed in dwUsage
                                                    // flag:
                                                    // RESOURCEUSAGE_ATTACHED.
                                                    // Note: handle will be null
                                                    // after return from 
                                                    // NwrCloseEnum

                                                    NwrCloseEnum( (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );

                                                    status = WN_NOT_AUTHENTICATED;
                                                }
                                                else
                                                {
                                                    // else the server does not 
                                                    // belong to us.
                                                    status = WN_BAD_NETNAME;
                                                }
                                                break;
                                            }
#endif
                                        } // else, this is a bindery server and
                                          // client does not care whether we
                                          // are bindery authenticated.

                                        if ( ( dwType == RESOURCETYPE_ANY ) ||
                                             ( ( dwType & RESOURCETYPE_DISK ) &&
                                               ( dwType & RESOURCETYPE_PRINT ) ) )
                                        { 
                                            status = NwrOpenEnumVolumesQueues(
                                                           NULL,
                                                           pszServerName,
                                                           (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                        }
                                        else if ( dwType & RESOURCETYPE_DISK ) 
                                        {
                                            status = NwrOpenEnumVolumes(
                                                             NULL,
                                                             pszServerName,
                                                             (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                        } 
                                        else if ( dwType & RESOURCETYPE_PRINT )
                                        {
                                            status = NwrOpenEnumQueues(
                                                             NULL,
                                                             pszServerName,
                                                             (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
                                        }
                                    }
                                    else
                                    {
                                        LPWSTR CachedUserName = NULL ;
                                        LPWSTR CachedPassword = NULL ;

#ifdef NT1057  // Make OpenEnum not interactive on SUR
                                        (void) NwpRetrieveCachedCredentials( pszRemoteName,
                                                                             &CachedUserName,
                                                                             &CachedPassword );

#endif
                                        status = NwrOpenEnumDirectories( 
                                                             NULL,
                                                             pszRemoteName,
                                                             CachedUserName,
                                                             CachedPassword,
                                                             (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );

#ifndef NT1057  // Make OpenEnum not interactive on SUR
                                        if (  (status == ERROR_INVALID_PASSWORD)
                                           || (status == ERROR_NO_SUCH_USER )
                                           )
                                        {
                                            status = WN_NOT_AUTHENTICATED;
                                            break;
                                        }

#else
                                        if ( CachedUserName )
                                        {
                                            (void) LocalFree( (HLOCAL) CachedUserName );
                                        }

                                        if ( CachedPassword )
                                        {
                                            RtlZeroMemory( CachedPassword,
                                                           wcslen(CachedPassword) *
                                                           sizeof( WCHAR ) );

                                            (void) LocalFree( ( HLOCAL ) CachedPassword );
                                        }

                                        if ( ( status == ERROR_INVALID_PASSWORD ) ||
                                             ( status == ERROR_NO_SUCH_USER ) )
                                        {
                                            LPWSTR UserName;
                                            LPWSTR Password;
                                            LPWSTR TmpPtr;

                                            //
                                            // Put up dialog to get username
                                            // and password.
                                            //
                                            status = NwpGetUserCredential( NULL,
                                                                     tempStrPtr,
                                                                     status,
                                                                     NULL,
                                                                     &UserName,
                                                                     &Password);

                                            if ( status == NO_ERROR )
                                            {
                                                status = NwrOpenEnumDirectories(
                                                             NULL,
                                                             pszRemoteName,
                                                             UserName,
                                                             Password,
                                                             (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );

                                                if ( status == NO_ERROR )
                                                {
                                                    status = NwpCacheCredentials(
                                                                 pszRemoteName,
                                                                 UserName,
                                                                 Password ) ;
                                                }

                                                (void) LocalFree( UserName );
                                       
                                                //
                                                // Clear the password
                                                //
                                                TmpPtr = Password;
                                                while ( *TmpPtr != 0 )
                                                    *TmpPtr++ = 0;

                                                (void) LocalFree( Password );
                                            }
                                            else if ( status == ERROR_WINDOW_NOT_DIALOG )
                                            {
                                                //
                                                // Caller is not a GUI app.
                                                //
                                                status = ERROR_INVALID_PASSWORD;
                                            }
                                            else if ( status == WN_CANCEL )
                                            {
                                                //
                                                // Cancel was pressed but we still
                                                // have to return success or MPR
                                                // will popup the error.  Return
                                                // a bogus enum handle.
                                                //
                                                *lphEnum = (HANDLE) 0xFFFFFFFF;
                                                status = NO_ERROR;
                                            }
                                        }
#endif
                                    }
                                }
                                else
                                {
                                    status = WN_BAD_NETNAME;
                                }
                            }
                        }
                        else
                        {
                            status = WN_BAD_VALUE;
                        }

                        if ( pszRemoteName != NULL )
                            LocalFree( (HLOCAL) pszRemoteName );
                    }

                    break;

                default:
                    KdPrint(("NWPROVIDER: Invalid dwScope %lu\n", dwScope));
                    status = WN_BAD_VALUE;
            } // end switch
        }
        else
        {
             status = WN_BAD_VALUE;
        }
    }
    RpcExcept( 1 )
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if ( status == ERROR_FILE_NOT_FOUND )
        status = WN_BAD_NETNAME;

    if ( status != NO_ERROR )
    {
        SetLastError( status );
    }

    return status;
}


DWORD
APIENTRY
NPEnumResource(
    HANDLE hEnum,
    LPDWORD lpcCount,
    LPVOID lpBuffer,
    LPDWORD lpBufferSize
    )
/*++

Routine Description:

    This function returns a lists of objects within the container
    specified by the enumeration context handle.

Arguments:

    hEnum - Supplies the resumable enumeration context handle.

        NOTE: If this value is 0xFFFFFFFF, it is not a context
              handle and this routine is required to return
              WN_NO_MORE_ENTRIES.  This hack is to handle the
              case where the user cancelled out of the network
              credential dialog on NwrOpenEnumDirectories and we
              cannot return an error there or we generate an error
              popup.

    lpcCount - On input, supplies the number of entries to get.
      On output, if NO_ERROR is returned, receives the number
      of entries NETRESOURCE returned in lpBuffer.

    lpBuffer - Receives an array of NETRESOURCE entries, each
        entry describes an object within the container.

    lpBufferSize - On input, supplies the size of lpBuffer in
        bytes.  On output, if WN_MORE_DATA is returned, receives
        the number of bytes needed in the buffer to get the
        next entry.

Return Value:


    NO_ERROR - Successfully returned at least one entry.

    WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
        is returned.

    WN_MORE_DATA - lpBuffer is too small to even get one entry.

    WN_BAD_HANDLE - The enumeration handle is invalid.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;
    DWORD BytesNeeded = 0;
    DWORD EntriesRead = 0;

#if DBG
    IF_DEBUG(ENUM) {
        KdPrint(("\nNWPROVAU: NPEnumResource\n"));
    }
#endif

    RpcTryExcept {

        if (hEnum == (HANDLE) 0xFFFFFFFF) {
            status = WN_NO_MORE_ENTRIES;
            goto EndOfTry;
        }

        status = NwrEnum(
                     (NWWKSTA_CONTEXT_HANDLE) hEnum,
                     *lpcCount,
                     (LPBYTE) lpBuffer,
                     *lpBufferSize,
                     &BytesNeeded,
                     &EntriesRead
                     );

        if (status == WN_MORE_DATA) {

            //
            // Output buffer too small to fit a single entry.
            //
            *lpBufferSize = BytesNeeded;
        }
        else if (status == NO_ERROR) {
            *lpcCount = EntriesRead;
        }

EndOfTry: ;

    }
    RpcExcept(1) {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
        SetLastError(status);
    }
    else 
    {

        //
        // Convert offsets of strings to pointers
        //
        if (EntriesRead > 0) {
    
            DWORD i;
            LPNETRESOURCEW NetR;


            NetR = lpBuffer;

            for (i = 0; i < EntriesRead; i++, NetR++) {

                if (NetR->lpLocalName != NULL) {
                    NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                                  (DWORD_PTR) NetR->lpLocalName);
                }

                NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                               (DWORD_PTR) NetR->lpRemoteName);

                if (NetR->lpComment != NULL) {
                    NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                                (DWORD_PTR) NetR->lpComment);
                }

                if (NetR->lpProvider != NULL) {
                    NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                                 (DWORD_PTR) NetR->lpProvider);
                }
            }
        }
    }

    return status;
}


DWORD
APIENTRY
NPGetResourceInformation(
    LPNETRESOURCEW lpNetResource,
    LPVOID         lpBuffer,
    LPDWORD        cbBuffer,
    LPWSTR       * lplpSystem
    )
/*++

Routine Description:

    This function returns an object which details information
    about a specified network resource.

Arguments:

    lpNetResource - This specifies the network resource for which the
        information is required. The lpRemoteName field of the NETRESOURCE
        specifies the remote name of the network resource whose information
        is required. If the calling program knows the values for the 
        lpProvider and dwType fields, then it should fill them in, otherwise,
        it should set them to NULL. All other fields in the NETRESOURCE are
        ignored and are not initialized.

    lpBuffer - A pointer to the buffer to receive the result, which is
        returned as a single NETRESOURCE entry representing the parent
        resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
        are returned, all other fields being set to NULL. The remote name
        returned should be in the same syntax as that returned from an 
        enumeration, so that the caller can do a case sensitive string 
        compare to determine whether an enumerated resource is this resource.
        If the provider owns a parent of the network resource, (in other
        words is known to be the correct network to respond to this request),
        then lpProvider should be filled in with a non-null entry. If it is
        known that a network owns a parent of the resource, but that the 
        resource itself is not valid, then lpProvider is returned as a 
        non-null value together with a return status of WN_BAD_VALUE. dwScope
        is returned as RESOURCE_CONTEXT if the network resource is part of
        the user's network context, otherwise it is returned as zero.

    cbBuffer - This specifies the size in bytes of the buffer passed to the
        function call. If the result is WN_MORE_DATA, this will contain the
        buffer size required (in bytes) to hold the NETRESOURCE information.

    lplpSystem - Returned pointer to a string in the buffer pointed to by
        lpBuffer that specifies the part of the resource that is accessed
        through resource type specific system APIs rather than WNet APIs.
        For example, if the input remote resource name was
        "\\server\share\dir", then lpRemoteName is returned pointing to 
        "\\server\share" and lplpSystem points to "\dir", both strings
        being stored in the buffer pointed to by lpBuffer.

Return Value:


    WN_SUCCESS - If the call is successful.

    WN_MORE_DATA - If input buffer is too small.

    WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
        of parameters is specified (e.g. lpRemoteName does not correspond 
        to dwType).

    WN_BAD_NETNAME - The resource is not recognized by this provider.

--*/
{
    DWORD  status;
    LPWSTR pszRemoteName = NULL;
    DWORD  BytesNeeded = 0;
    DWORD  SystemOffset = 0;

    *lplpSystem = NULL;

    status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return status;
    }

#if DBG
    IF_DEBUG(CONNECT)
    {
        KdPrint(("\nNWPROVAU: NPGetResourceInformation %ws\n", pszRemoteName));
    }
#endif

    RpcTryExcept
    {
        if (lpNetResource->dwType != RESOURCETYPE_ANY &&
            lpNetResource->dwType != RESOURCETYPE_DISK &&
            lpNetResource->dwType != RESOURCETYPE_PRINT)
        {
            status = WN_BAD_VALUE;
        }
        else
        {
            status = NwrGetResourceInformation(
                        NULL,
                        pszRemoteName,
                        lpNetResource->dwType,
                        (LPBYTE) lpBuffer,
                        *cbBuffer,
                        &BytesNeeded,
                        &SystemOffset
                        );

            if (status == WN_MORE_DATA)
            {
                //
                // Output buffer too small.
                //
                *cbBuffer = BytesNeeded;
            }
        }
    }
    RpcExcept(1)
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept
    if ( pszRemoteName )
        LocalFree( (HLOCAL) pszRemoteName );

    if (status != NO_ERROR)
    {
        SetLastError(status);
    }
    else 
    {
        //
        // Convert offsets of strings to pointers
        //
        DWORD i;
        LPNETRESOURCEW NetR = lpBuffer;

        if (NetR->lpLocalName != NULL)
        {
            NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                          (DWORD_PTR) NetR->lpLocalName);
        }

        NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                       (DWORD_PTR) NetR->lpRemoteName);

        if (NetR->lpComment != NULL)
        {
            NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                        (DWORD_PTR) NetR->lpComment);
        }

        if (NetR->lpProvider != NULL)
        {
            NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                         (DWORD_PTR) NetR->lpProvider);
        }

        if (SystemOffset != 0)
        {
            *lplpSystem = (LPWSTR) ((DWORD_PTR) lpBuffer + SystemOffset);
        }
    }

    return status;
}



DWORD
APIENTRY
NPGetResourceParent(
    LPNETRESOURCEW lpNetResource,
    LPVOID         lpBuffer,
    LPDWORD        cbBuffer
    )
/*++

Routine Description:

    This function returns an object which details information
    about the parent of a specified network resource.

Arguments:

    lpNetResource - This specifies the network resource for which the
        parent name is required. The NETRESOURCE could have been obtained via 
        previous NPEnumResource, or constructed by the caller. The lpRemoteName
        field of the NETRESOURCE specifies the remote name of the network 
        resouce whose parent name is required. If the calling program knows
        the values for the lpProvider and dwType fields, then it can fill
        them in, otherwise, they are set to NULL. If the lpProvider field is
        not NULL, then the network provider DLL can assume that the resource
        is owned by its network, but if it is NULL, then it must assume
        that the resource could be for some other network and do whatever
        checking is neccessary to ensure that the result returned is accurate.
        For example, if being asked for the parent of a server, and the server
        is not part of a workgroup, the the network provider DLL should check
        to ensure that the server is part of its network and, if so, return
        its provider name. All other fields in the NETRESOURCE are ignored and
        are not initialized.

    lpBuffer - A pointer to the buffer to receive the result, which is 
        returned as a single NETRESOURCE entry representing the parent
        resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
        are returned, all other fields being set to NULL. lpProvider should
        be set to NULL if the provider has only done a syntactic check (i.e.
        does not know that the resource is specific to its network). If the
        provider owns a parent of the network resource, (in other words is
        known to be the correct network to respond to this request), then
        lpProvider should be filled in with a non-null entry, even if the
        return is WN_BAD_VALUE. The remote name returned should be in the
        same syntax as that returned from an enumeration, so that the caller
        can do a case sensitive string compare to determine whether an
        enumerated resource is this resource. If a resource has no browse
        parent on the network, the lpRemoteName is returned as NULL. The
        RESOURCEUSAGE_CONNECTABLE value in the dwUsage field does not
        indicate that the resource can currently be connected to, but that
        the resource is connectable when it is available on the network.

    cbBuffer - This specifies the size in bytes of the buffer passed to the
        function call. If the result is WN_MORE_DATA, this will contain the
        buffer size required (in bytes) to hold the NETRESOURCE information.

Return Value:

    WN_SUCCESS - If the call is successful.

    WN_MORE_DATA - If input buffer is too small.

    WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
        of parameters is specified (e.g. lpRemoteName does not correspond 
        to dwType).

--*/
{
    DWORD  status;
    LPWSTR pszRemoteName = NULL;
    DWORD  BytesNeeded = 0;

    status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return status;
    }

#if DBG
    IF_DEBUG(CONNECT)
    {
        KdPrint(("\nNWPROVAU: NPGetResourceParent %ws\n", pszRemoteName));
    }
#endif

    RpcTryExcept
    {
        if (lpNetResource->dwType != RESOURCETYPE_ANY &&
            lpNetResource->dwType != RESOURCETYPE_DISK &&
            lpNetResource->dwType != RESOURCETYPE_PRINT)
        {
            status = WN_BAD_VALUE;
        }
        else
        {
            status = NwrGetResourceParent(
                        NULL,
                        pszRemoteName,
                        lpNetResource->dwType,
                        (LPBYTE) lpBuffer,
                        *cbBuffer,
                        &BytesNeeded
                        );

            if (status == WN_MORE_DATA)
            {
                //
                // Output buffer too small.
                //
                *cbBuffer = BytesNeeded;
            }
        }
    }
    RpcExcept(1)
    {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept
    if ( pszRemoteName )
        LocalFree( (HLOCAL) pszRemoteName );


    if (status != NO_ERROR)
    {
        SetLastError(status);
    }
    else 
    {
        //
        // Convert offsets of strings to pointers
        //
        DWORD i;
        LPNETRESOURCEW NetR = lpBuffer;

        if (NetR->lpLocalName != NULL)
        {
            NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                          (DWORD_PTR) NetR->lpLocalName);
        }

        NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                       (DWORD_PTR) NetR->lpRemoteName);

        if (NetR->lpComment != NULL)
        {
            NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                        (DWORD_PTR) NetR->lpComment);
        }

        if (NetR->lpProvider != NULL)
        {
            NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                         (DWORD_PTR) NetR->lpProvider);
        }
    }

    return status;
}



DWORD
APIENTRY
NwEnumConnections(
    HANDLE hEnum,
    LPDWORD lpcCount,
    LPVOID lpBuffer,
    LPDWORD lpBufferSize,
    BOOL    fImplicitConnections
    )
/*++

Routine Description:

    This function returns a lists of connections.

Arguments:

    hEnum - Supplies the resumable enumeration context handle.

        NOTE: If this value is 0xFFFFFFFF, it is not a context
              handle and this routine is required to return
              WN_NO_MORE_ENTRIES.  This hack is to handle the
              case where the user cancelled out of the network
              credential dialog on NwrOpenEnumDirectories and we
              cannot return an error there or we generate an error
              popup.

    lpcCount - On input, supplies the number of entries to get.
      On output, if NO_ERROR is returned, receives the number
      of entries NETRESOURCE returned in lpBuffer.

    lpBuffer - Receives an array of NETRESOURCE entries, each
        entry describes an object within the container.

    lpBufferSize - On input, supplies the size of lpBuffer in
        bytes.  On output, if WN_MORE_DATA is returned, receives
        the number of bytes needed in the buffer to get the
        next entry.

    fImplicitConnections - TRUE is we also want all implicit connections,
        FALSE otherwise.

Return Value:


    NO_ERROR - Successfully returned at least one entry.

    WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
        is returned.

    WN_MORE_DATA - lpBuffer is too small to even get one entry.

    WN_BAD_HANDLE - The enumeration handle is invalid.

    Other network errors.

--*/
{
    DWORD status = NO_ERROR;
    DWORD BytesNeeded = 0;
    DWORD EntriesRead = 0;

#if DBG
    IF_DEBUG(ENUM) {
        KdPrint(("\nNWPROVAU: NPEnumResource\n"));
    }
#endif

    RpcTryExcept {

        if (hEnum == (HANDLE) 0xFFFFFFFF) {
            status = WN_NO_MORE_ENTRIES;
            goto EndOfTry;
        }

        status = NwrEnumConnections(
                     (NWWKSTA_CONTEXT_HANDLE) hEnum,
                     *lpcCount,
                     (LPBYTE) lpBuffer,
                     *lpBufferSize,
                     &BytesNeeded,
                     &EntriesRead,
                     fImplicitConnections
                     );

        if (status == WN_MORE_DATA) {

            //
            // Output buffer too small to fit a single entry.
            //
            *lpBufferSize = BytesNeeded;
        }
        else if (status == NO_ERROR) {
            *lpcCount = EntriesRead;
        }

EndOfTry: ;

    }
    RpcExcept(1) {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
        SetLastError(status);
    }

    //
    // Convert offsets of strings to pointers
    //
    if (EntriesRead > 0) {

        DWORD i;
        LPNETRESOURCEW NetR;


        NetR = lpBuffer;

        for (i = 0; i < EntriesRead; i++, NetR++) {

            if (NetR->lpLocalName != NULL) {
                NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                              (DWORD_PTR) NetR->lpLocalName);
            }

            NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                           (DWORD_PTR) NetR->lpRemoteName);

            if (NetR->lpComment != NULL) {
                NetR->lpComment = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                            (DWORD_PTR) NetR->lpComment);
            }

            if (NetR->lpProvider != NULL) {
                NetR->lpProvider = (LPWSTR) ((DWORD_PTR) lpBuffer +
                                             (DWORD_PTR) NetR->lpProvider);
            }
        }
    }

    return status;
}


DWORD
APIENTRY
NPCloseEnum(
    HANDLE hEnum
    )
/*++

Routine Description:

    This function closes the enumeration context handle.

Arguments:

    hEnum - Supplies the enumeration context handle.

        NOTE: If this value is 0xFFFFFFFF, it is not a context
              handle.  Just return success.

Return Value:

    NO_ERROR - Successfully returned at least one entry.

    WN_BAD_HANDLE - The enumeration handle is invalid.

--*/
{
    DWORD status = NO_ERROR;

#if DBG
    IF_DEBUG(ENUM) {
        KdPrint(("\nNWPROVAU: NPCloseEnum\n"));
    }
#endif

    RpcTryExcept
    {
        if (hEnum == (HANDLE) 0xFFFFFFFF) {
            status = NO_ERROR;
        }
        else {
            status = NwrCloseEnum(
                        (LPNWWKSTA_CONTEXT_HANDLE) &hEnum
                        );
        }
    }
    RpcExcept(1) {
        status = NwpMapRpcError(RpcExceptionCode());
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
    }
    return status;
}


DWORD
APIENTRY
NPFormatNetworkName(
    LPWSTR lpRemoteName,
    LPWSTR lpFormattedName,
    LPDWORD lpnLength,
    DWORD dwFlags,
    DWORD dwAveCharPerLine
    )
/*++

Routine Description:

    This function takes a fully-qualified UNC name and formats it
    into a shorter form for display.  Only the name of the object
    within the container is returned for display.

    We only support formatting of the remote resource name to the
    abbreviated form for display during enumeration where the container
    name is displayed prior to the object within it.

Arguments:

    lpRemoteName - Supplies the fully-qualified UNC name.

    lpFormatedName - Output buffer to receive the formatted name.

    lpnLength - On input, supplies the length of the lpFormattedName
        buffer in characters.  On output, if WN_MORE_DATA is returned,
        receives the length in number of characters required of the
        output buffer to hold the formatted name.

    dwFlags - Supplies a bitwise set of flags indicating the type
        of formatting required on lpRemoteName.

    dwAveCharPerLine - Ignored.

Return Value:

    NO_ERROR - Successfully returned at least one entry.

    WN_MORE_DATA - lpFormattedName buffer is too small.

    WN_BAD_VALUE - lpRemoteName is NULL.

    ERROR_NOT_SUPPORTED - dwFlags that does not contain the
        WNFMT_INENUM bit.

--*/
{
    DWORD status = NO_ERROR;

    LPWSTR NextBackSlash;
    LPWSTR Source;
    DWORD SourceLen;

#if DBG
    IF_DEBUG(OTHER) 
        KdPrint(("\nNWPROVAU: NPFormatNetworkName\n"));
#endif

    if (lpRemoteName == NULL) 
    {
        status = WN_BAD_VALUE;
        goto CleanExit;
    }

    if (dwFlags & WNFMT_INENUM) 
    {
        WORD   i;
        WORD   length     = (WORD) wcslen( lpRemoteName );
        WORD   slashCount = 0;
        WORD   dotCount   = 0;
        WORD   Start      = 0;
        WORD   End        = length;
        BOOL   isNdsUnc   = FALSE;
        BOOL   couldBeNdsUnc = FALSE;

        if ( lpRemoteName[0] == L' ' )
            couldBeNdsUnc = TRUE;

        for ( i = 0; i < length; i++ )
        {
            if ( lpRemoteName[i] == L'\\' )
            {
                slashCount++;
                if ( i + 1 < length )
                {
                    Start = i + 1;
                }
            }

            if ( couldBeNdsUnc &&
                 ( ( lpRemoteName[i] == L'.' ) ||
                   ( lpRemoteName[i] == L'=' ) ) )
                isNdsUnc = TRUE;

            if ( dotCount < 1 && isNdsUnc && lpRemoteName[i] == L'.' )
            {
                End = i - 1;
                dotCount++;
            }
        }

        if ( i > length )
            End = length - 1;

        if ( slashCount > 3 || ( isNdsUnc != TRUE && slashCount != 3 && dotCount == 0 ) )
            End = i - 1;

        Source = &lpRemoteName[Start];
        SourceLen = End - Start + 1;

        if ( SourceLen + 1 > *lpnLength ) 
        {
            *lpnLength = SourceLen + 1;
            status = WN_MORE_DATA;
        }
        else 
        {
            wcsncpy( lpFormattedName, Source, SourceLen );
            lpFormattedName[SourceLen] = 0x00000000;
            status = NO_ERROR;
        }
    }
    else if ( dwFlags & WNFMT_MULTILINE ) 
    {

        DWORD i, j, k = 0; 
        DWORD nLastBackSlash = 0;
        DWORD BytesNeeded = ( wcslen( lpRemoteName ) + 1 +
                              2 * wcslen( lpRemoteName ) / dwAveCharPerLine
                            ) * sizeof( WCHAR); 

        if ( *lpnLength < (BytesNeeded/sizeof(WCHAR)) )
        {
            *lpnLength = BytesNeeded/sizeof(WCHAR);
            status = WN_MORE_DATA;
            goto CleanExit;
        }

        for ( i = 0, j = 0; lpRemoteName[i] != 0; i++, j++ )
        {
            if ( lpRemoteName[i] == L'\\' )
                nLastBackSlash = i;

            if ( k == dwAveCharPerLine )
            {
                if ( lpRemoteName[i] != L'\\' )
                {
                    DWORD m, n;
                    for ( n = nLastBackSlash, m = ++j ; n <= i ; n++, m-- )  
                    {
                        lpFormattedName[m] = lpFormattedName[m-1];
                    }
                    lpFormattedName[m] = L'\n';
                    k = i - nLastBackSlash - 1;
                }
                else
                {
                    lpFormattedName[j++] = L'\n';
                    k = 0;
                }
            }

            lpFormattedName[j] = lpRemoteName[i];
            k++;
        }

        lpFormattedName[j] = 0;

    }
    else if ( dwFlags & WNFMT_ABBREVIATED )
    {
        //
        // we dont support abbreviated form for now because we look bad
        // in comdlg (fileopen) if we do.
        //

        DWORD nLength;
        nLength = wcslen( lpRemoteName ) + 1 ;
        if (nLength >  *lpnLength)
        {
            *lpnLength = nLength;
            status = WN_MORE_DATA;
            goto CleanExit;
        }
        else
        {
            wcscpy( lpFormattedName, lpRemoteName ); 
        }

#if 0
        DWORD i, j, k;
        DWORD BytesNeeded = dwAveCharPerLine * sizeof( WCHAR); 
        DWORD nLength;

        if ( *lpnLength < BytesNeeded )
        {
            *lpnLength = BytesNeeded;
            status = WN_MORE_DATA;
            goto CleanExit;
        }

        nLength = wcslen( lpRemoteName );
        if ( ( nLength + 1) <= dwAveCharPerLine )
        {
            wcscpy( lpFormattedName, lpRemoteName ); 
        }
        else
        {
            lpFormattedName[0] = lpRemoteName[0];
            lpFormattedName[1] = lpRemoteName[1];

            for ( i = 2; lpRemoteName[i] != L'\\'; i++ )
                lpFormattedName[i] = lpRemoteName[i];

            for ( j = dwAveCharPerLine-1, k = nLength; j >= (i+3); j--, k-- )
            {
                lpFormattedName[j] = lpRemoteName[k];
                if ( lpRemoteName[k] == L'\\' )
                {
                    j--;
                    break;
                }
            }

            lpFormattedName[j] = lpFormattedName[j-1] = lpFormattedName[j-2] = L'.';
        
            for ( k = i; k < (j-2); k++ )
                lpFormattedName[k] = lpRemoteName[k];
            
        }

#endif 

    }     
    else   // some unknown flags
    {
        status = ERROR_NOT_SUPPORTED;
    }

CleanExit:

    if (status != NO_ERROR) 
        SetLastError(status);

    return status;
}


STATIC
BOOL
NwpWorkstationStarted(
    VOID
    )
/*++

Routine Description:

    This function queries the service controller to see if the
    NetWare workstation service has started.  If in doubt, it returns
    FALSE.

Arguments:

    None.

Return Value:

    Returns TRUE if the NetWare workstation service has started,
    FALSE otherwise.

--*/
{
    SC_HANDLE ScManager;
    SC_HANDLE Service;
    SERVICE_STATUS ServiceStatus;
    BOOL IsStarted = FALSE;

    ScManager = OpenSCManagerW(
                    NULL,
                    NULL,
                    SC_MANAGER_CONNECT
                    );

    if (ScManager == NULL) {
        return FALSE;
    }

    Service = OpenServiceW(
                  ScManager,
                  NW_WORKSTATION_SERVICE,
                  SERVICE_QUERY_STATUS
                  );

    if (Service == NULL) {
        CloseServiceHandle(ScManager);
        return FALSE;
    }

    if (! QueryServiceStatus(Service, &ServiceStatus)) {
        CloseServiceHandle(ScManager);
        CloseServiceHandle(Service);
        return FALSE;
    }


    if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
         (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
         (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
         (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {

        IsStarted = TRUE;
    }

    CloseServiceHandle(ScManager);
    CloseServiceHandle(Service);

    return IsStarted;
}



DWORD
NwpMapNameToUNC(
    IN  LPWSTR pszName,
    OUT LPWSTR *ppszUNC 
    )
/*++

Routine Description:

    This routine validates the given name as a netwarepath or UNC path. 
    If it is a netware path, this routine will convert the 
    Netware path name to UNC name. 

Arguments:

    pszName - Supplies the netware name or UNC name
    ppszUNC - Points to the converted UNC name

Return Value:

    NO_ERROR or the error that occurred.

--*/
{
    DWORD err = NO_ERROR;

    LPWSTR pszSrc = pszName;
    LPWSTR pszDest;

    BOOL fSlash = FALSE;
    BOOL fColon = FALSE;
    DWORD nServerLen = 0;
    DWORD nVolLen = 0;
    BOOL fFirstToken = TRUE;

    *ppszUNC = NULL;
                               
    //
    // The name cannot be NULL or empty string
    //
    if ( pszName == NULL || *pszName == 0) 
        return WN_BAD_NETNAME;

#if DBG
    IF_DEBUG(CONNECT) 
        KdPrint(("NwpMapNameToUNC: Source = %ws\n", pszName ));
#endif

    //
    // Get rid of the <space> if a NDS tree name ...
    //
    if ( pszName[0] == L' ' &&
         pszName[1] == L'\\' &&
         pszName[2] == L'\\' )
        pszName = &pszName[1];

    //
    // Check if the given name is a valid UNC name
    //
    err = NwLibCanonRemoteName( NULL,     // "\\Server" is valid UNC path
                                pszName,
                                ppszUNC,
                                NULL );

    //
    // The given name is a valid UNC name, so return success!
    //
    if ( err == NO_ERROR )
        return err;

    //
    // Allocate the buffer to store the mapped UNC name
    // We allocate 3 extra characters, two for the backslashes in front
    // and one for the ease of parsing below.
    //
    if ((*ppszUNC = (LPVOID) LocalAlloc( 
                                 LMEM_ZEROINIT,
                                 (wcslen( pszName) + 4) * sizeof( WCHAR)
                                 )) == NULL )
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    wcscpy( *ppszUNC, L"\\\\" );
    pszDest = *ppszUNC + 2;   // Skip past two backslashes

    //
    // Parse the given string and put the converted string into *ppszUNC
    // In the converted string, we will substitute 0 for all slashes
    // for the time being.
    //
    for ( ; *pszSrc != 0; pszSrc++ )
    { 
        if (  ( *pszSrc == L'/' )
           || ( *pszSrc == L'\\' )
           )
        {
            //
            // Two consecutive backslashes are bad
            //
            if ( (*(pszSrc+1) ==  L'/') ||  (*(pszSrc+1) == L'\\'))
            {
                LocalFree( *ppszUNC );
                *ppszUNC = NULL;
                return WN_BAD_NETNAME;
            }

            if ( !fSlash )
                fSlash = TRUE;

            *pszDest++ = 0;
        }
        else if ( (*pszSrc == L':') && fSlash && !fColon )
        {
            fColon = TRUE;
            if ( *(pszSrc+1) != 0 )
                *pszDest++ = 0;
 
        }
        else
        {
            *pszDest++ = *pszSrc;
            if (( fSlash ) && ( !fColon))
                nVolLen++; 
            else if ( !fSlash )
                nServerLen++; 
        }
    }

    //
    // Note: *ppszUNC is already terminated with two '\0' because we initialized
    //       the whole buffer to zero.
    // 

    if (  ( nServerLen == 0 )
       || ( fSlash && nVolLen == 0 )
       || ( fSlash && nVolLen != 0 && !fColon )
       )
    {
        LocalFree( *ppszUNC );
        *ppszUNC = NULL;
        return WN_BAD_NETNAME;
    }

    //
    // At this point, we know the name is a valid Netware syntax
    //     i.e. SERVER[/VOL:/dir]
    // We now need to validate that all the characters used in the
    // servername, volume, directory are valid characters
    //

    pszDest = *ppszUNC + 2;   // Skip past the first two backslashes
    while ( *pszDest != 0 )
    {
         DWORD nLen = wcslen( pszDest );
         
         if (  ( fFirstToken &&  !IS_VALID_SERVER_TOKEN( pszDest, nLen )) 
            || ( !fFirstToken && !IS_VALID_TOKEN( pszDest, nLen )) 
            )
         { 
             LocalFree( *ppszUNC );
             *ppszUNC = NULL;
             return WN_BAD_NETNAME;
         }
     
         fFirstToken = FALSE;
         pszDest += nLen + 1;
    }

    //
    // The netware name is valid! Convert 0 back to backslash in 
    // converted string.
    //

    pszDest = *ppszUNC + 2;   // Skip past the first two backslashes
    while ( *pszDest != 0 )
    {
        if ( (*(pszDest+1) == 0 ) && (*(pszDest+2) != 0 ) )
        {
            *(pszDest+1) = L'\\';
        }
        pszDest++;
    }
                  
#if DBG
    IF_DEBUG(CONNECT) 
        KdPrint(("NwpMapNameToUNC: Destination = %ws\n", *ppszUNC ));
#endif
    return NO_ERROR;
}


STATIC
VOID
NwpGetUncInfo(
    IN LPWSTR lpstrUnc,
    OUT WORD * slashCount,
    OUT BOOL * isNdsUnc
    )
{
    WORD   i;
    WORD   length = (WORD) wcslen( lpstrUnc );

    *isNdsUnc = FALSE;
    *slashCount = 0;

    for ( i = 0; i < length; i++ )
    {
        if ( ( lpstrUnc[i] == L'.' ) && ( *slashCount == 3 ) )
        {
            *isNdsUnc = TRUE;
        }

        if ( lpstrUnc[i] == L'\\' )
        {
            *slashCount += 1;
        }
    }
}


STATIC
LPWSTR
NwpGetUncObjectName(
    IN LPWSTR ContainerName
)
{
    WORD length = 2;
    WORD totalLength = (WORD) wcslen( ContainerName );

    if ( totalLength < 2 )
        return 0;

    while ( length < totalLength )
    {
        if ( ContainerName[length] == L'.' )
            ContainerName[length] = L'\0';

        length++;
    }

    length = 2;

    while ( length < totalLength && ContainerName[length] != L'\\' )
    {
        length++;
    }

    if ( ( ContainerName[length + 1] == L'C' ||
           ContainerName[length + 1] == L'c' ) &&
         ( ContainerName[length + 2] == L'N' ||
           ContainerName[length + 2] == L'n' ) &&
         ContainerName[length + 3] == L'=' )
    {
        ContainerName[length + 2] = L'\\';
        ContainerName[length + 3] = L'\\';

        return (ContainerName + length + 2);
    }

    ContainerName[length - 1] = L'\\';

    return (ContainerName + length - 1);
}
    

STATIC
WORD
NwpGetSlashCount(
    IN LPWSTR lpstrUnc
    )
{
    WORD   count = 0;
    WORD   i;
    WORD   length = (WORD) wcslen( lpstrUnc );

    for ( i = 0; i < length; i++ )
    {
        if ( lpstrUnc[i] == L'\\' )
        {
            count++;
        }
    }

    return count;
}


DWORD
NwpMapRpcError(
    IN DWORD RpcError
    )
/*++

Routine Description:

    This routine maps the RPC error into a more meaningful windows
    error for the caller.

Arguments:

    RpcError - Supplies the exception error raised by RPC

Return Value:

    Returns the mapped error.

--*/
{

    switch (RpcError) {

        case RPC_S_UNKNOWN_IF:
        case RPC_S_SERVER_UNAVAILABLE:
            return WN_NO_NETWORK;

        case RPC_S_INVALID_BINDING:
        case RPC_X_SS_IN_NULL_CONTEXT:
        case RPC_X_SS_CONTEXT_DAMAGED:
        case RPC_X_SS_HANDLES_MISMATCH:
        case ERROR_INVALID_HANDLE:
            return ERROR_INVALID_HANDLE;

        case RPC_X_NULL_REF_POINTER:
            return ERROR_INVALID_PARAMETER;

        case EXCEPTION_ACCESS_VIOLATION:
            return ERROR_INVALID_ADDRESS;

        default:
            return RpcError;
    }
}