/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    api.c

Abstract:

    This module contains misc APIs that are used by the
    NWC wksta.

Author:

    ChuckC         2-Mar-94        Created


Revision History:

--*/


#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <nwcons.h>
#include <nwmisc.h>
#include <nwapi32.h>
#include "nwstatus.h"
#include "nwevent.h"

DWORD
NwMapStatus(
    IN  NTSTATUS NtStatus
    );

DWORD
NwOpenPreferredServer(
    PHANDLE ServerHandle
    );

NTSTATUS
NwOpenHandle(
    IN PUNICODE_STRING ObjectName,
    IN BOOL ValidateFlag,
    OUT PHANDLE ObjectHandle
    );

NTSTATUS
NwCallNtOpenFile(
    OUT PHANDLE ObjectHandle,
    IN ACCESS_MASK DesiredAccess,
    IN PUNICODE_STRING ObjectName,
    IN ULONG OpenOptions
   );


//
// list of error mappings known for E3H calls. we do not have a single list
// because Netware reuses the numbers depending on call.
//

typedef struct _ERROR_MAP_ENTRY
{
    UCHAR NetError;
    NTSTATUS ResultingStatus;
}  ERROR_MAP_ENTRY ;

ERROR_MAP_ENTRY Error_Map_Bindery[] =
{

    //
    //  NetWare specific error mappings. Specific to E3H.
    //
    {  1, STATUS_DISK_FULL },
    {128, STATUS_SHARING_VIOLATION },
    {129, STATUS_INSUFF_SERVER_RESOURCES },
    {130, STATUS_ACCESS_DENIED },
    {131, STATUS_DATA_ERROR },
    {132, STATUS_ACCESS_DENIED },
    {133, STATUS_ACCESS_DENIED },
    {134, STATUS_ACCESS_DENIED },
    {135, STATUS_OBJECT_NAME_INVALID },
    {136, STATUS_INVALID_HANDLE },
    {137, STATUS_ACCESS_DENIED },
    {138, STATUS_ACCESS_DENIED },
    {139, STATUS_ACCESS_DENIED },
    {140, STATUS_ACCESS_DENIED },
    {141, STATUS_SHARING_VIOLATION },
    {142, STATUS_SHARING_VIOLATION },
    {143, STATUS_ACCESS_DENIED },
    {144, STATUS_ACCESS_DENIED },
    {145, STATUS_OBJECT_NAME_COLLISION },
    {146, STATUS_OBJECT_NAME_COLLISION },
    {147, STATUS_ACCESS_DENIED },
    {148, STATUS_ACCESS_DENIED },
    {150, STATUS_INSUFF_SERVER_RESOURCES },
    {151, STATUS_NO_SPOOL_SPACE },
    {152, STATUS_NO_SUCH_DEVICE },
    {153, STATUS_DISK_FULL },
    {154, STATUS_NOT_SAME_DEVICE },
    {155, STATUS_INVALID_HANDLE },
    {156, STATUS_OBJECT_PATH_NOT_FOUND },
    {157, STATUS_INSUFF_SERVER_RESOURCES },
    {158, STATUS_OBJECT_PATH_INVALID },
    {159, STATUS_SHARING_VIOLATION },
    {160, STATUS_DIRECTORY_NOT_EMPTY },
    {161, STATUS_DATA_ERROR },
    {162, STATUS_SHARING_VIOLATION },
    {192, STATUS_ACCESS_DENIED },
    {198, STATUS_ACCESS_DENIED },
    {211, STATUS_ACCESS_DENIED },
    {212, STATUS_PRINT_QUEUE_FULL },
    {213, STATUS_PRINT_CANCELLED },
    {214, STATUS_ACCESS_DENIED },
    {215, STATUS_PASSWORD_RESTRICTION },
    {216, STATUS_PASSWORD_RESTRICTION },
    {220, STATUS_ACCOUNT_DISABLED },
    {222, STATUS_PASSWORD_EXPIRED },
    {223, STATUS_PASSWORD_EXPIRED },
    {239, STATUS_OBJECT_NAME_INVALID },
    {240, STATUS_OBJECT_NAME_INVALID },
    {251, STATUS_INVALID_PARAMETER },
    {252, STATUS_NO_MORE_ENTRIES },
    {253, STATUS_FILE_LOCK_CONFLICT },
    {254, STATUS_FILE_LOCK_CONFLICT },
    {255, STATUS_UNSUCCESSFUL}
};


ERROR_MAP_ENTRY Error_Map_General[] =
{
    {  1, STATUS_DISK_FULL },
    {128, STATUS_SHARING_VIOLATION },
    {129, STATUS_INSUFF_SERVER_RESOURCES },
    {130, STATUS_ACCESS_DENIED },
    {131, STATUS_DATA_ERROR },
    {132, STATUS_ACCESS_DENIED },
    {133, STATUS_ACCESS_DENIED },
    {134, STATUS_ACCESS_DENIED },
    {135, STATUS_OBJECT_NAME_INVALID },
    {136, STATUS_INVALID_HANDLE },
    {137, STATUS_ACCESS_DENIED },
    {138, STATUS_ACCESS_DENIED },
    {139, STATUS_ACCESS_DENIED },
    {140, STATUS_ACCESS_DENIED },
    {141, STATUS_SHARING_VIOLATION },
    {142, STATUS_SHARING_VIOLATION },
    {143, STATUS_ACCESS_DENIED },
    {144, STATUS_ACCESS_DENIED },
    {145, STATUS_OBJECT_NAME_COLLISION },
    {146, STATUS_OBJECT_NAME_COLLISION },
    {147, STATUS_ACCESS_DENIED },
    {148, STATUS_ACCESS_DENIED },
    {150, STATUS_INSUFF_SERVER_RESOURCES },
    {151, STATUS_NO_SPOOL_SPACE },
    {152, STATUS_NO_SUCH_DEVICE },
    {153, STATUS_DISK_FULL },
    {154, STATUS_NOT_SAME_DEVICE },
    {155, STATUS_INVALID_HANDLE },
    {156, STATUS_OBJECT_PATH_NOT_FOUND },
    {157, STATUS_INSUFF_SERVER_RESOURCES },
    {158, STATUS_OBJECT_PATH_INVALID },
    {159, STATUS_SHARING_VIOLATION },
    {160, STATUS_DIRECTORY_NOT_EMPTY },
    {161, STATUS_DATA_ERROR },
    {162, STATUS_SHARING_VIOLATION },
    {192, STATUS_ACCESS_DENIED },
    {198, STATUS_ACCESS_DENIED },
    {211, STATUS_ACCESS_DENIED },
    {212, STATUS_PRINT_QUEUE_FULL },
    {213, STATUS_PRINT_CANCELLED },
    {214, STATUS_ACCESS_DENIED },
    {215, STATUS_DEVICE_BUSY },
    {216, STATUS_DEVICE_DOES_NOT_EXIST },
    {220, STATUS_ACCOUNT_DISABLED },
    {222, STATUS_PASSWORD_EXPIRED },
    {223, STATUS_PASSWORD_EXPIRED },
    {239, STATUS_OBJECT_NAME_INVALID },
    {240, STATUS_OBJECT_NAME_INVALID },
    {251, STATUS_INVALID_PARAMETER },
    {252, STATUS_NO_MORE_ENTRIES },
    {253, STATUS_FILE_LOCK_CONFLICT },
    {254, STATUS_FILE_LOCK_CONFLICT },
    {255, STATUS_UNSUCCESSFUL}
};

#define NUM_ERRORS(x)  (sizeof(x)/sizeof(x[0]))

DWORD
NwMapBinderyCompletionCode(
    IN  NTSTATUS NtStatus
    )
/*++

Routine Description:

    This function takes a bindery completion code embedded in an NT status
    code and maps it to the appropriate Win32 error code. Used specifically
    for E3H operations.

Arguments:

    NtStatus - Supplies the NT status (that contains the code in low 16 bits)

Return Value:

    Returns the appropriate Win32 error.

--*/
{
    DWORD i; UCHAR code ;

    //
    // A small optimization for the most common case.
    //
    if (NtStatus == STATUS_SUCCESS)
        return NO_ERROR;

    //
    // Map connection errors specially.
    //

    if ( ( (NtStatus & 0xFFFF0000) == 0xC0010000) &&
         ( (NtStatus & 0xFF00) != 0 ) )
    {
        return ERROR_UNEXP_NET_ERR;
    }

    //
    // if facility code not set, assume it is NT Status
    //
    if ( (NtStatus & 0xFFFF0000) != 0xC0010000)
        return RtlNtStatusToDosError(NtStatus);

    code = (UCHAR)(NtStatus & 0x000000FF);
    for (i = 0; i < NUM_ERRORS(Error_Map_Bindery); i++)
    {
        if (Error_Map_Bindery[i].NetError == code)
            return( NwMapStatus(Error_Map_Bindery[i].ResultingStatus));
    }

    //
    // if cannot find let NwMapStatus do its best
    //
    return NwMapStatus(NtStatus);
}



DWORD
NwMapStatus(
    IN  NTSTATUS NtStatus
    )
/*++

Routine Description:

    This function takes an NT status code and maps it to the appropriate
    Win32 error code. If facility code is set, assume it is NW specific

Arguments:

    NtStatus - Supplies the NT status.

Return Value:

    Returns the appropriate Win32 error.

--*/
{
    DWORD i; UCHAR code ;

    //
    // A small optimization for the most common case.
    //
    if (NtStatus == STATUS_SUCCESS)
        return NO_ERROR;

    //
    // Map connection errors specially.
    //

    if ( ( (NtStatus & 0xFFFF0000) == 0xC0010000) &&
         ( (NtStatus & 0xFF00) != 0 ) )
    {
        return ERROR_UNEXP_NET_ERR;
    }

    //
    // if facility code set, assume it is NW Completion code
    //
    if ( (NtStatus & 0xFFFF0000) == 0xC0010000)
    {
        code = (UCHAR)(NtStatus & 0x000000FF);
        for (i = 0; i < NUM_ERRORS(Error_Map_General); i++)
        {
            if (Error_Map_General[i].NetError == code)
            {
                //
                // map it to NTSTATUS and then drop thru to map to Win32
                //
                NtStatus = Error_Map_General[i].ResultingStatus ;
                break ;
            }
        }
    }

    switch (NtStatus) {
        case STATUS_OBJECT_NAME_COLLISION:
            return ERROR_ALREADY_ASSIGNED;

        case STATUS_OBJECT_NAME_NOT_FOUND:
            return ERROR_NOT_CONNECTED;

        case STATUS_IMAGE_ALREADY_LOADED:
        case STATUS_REDIRECTOR_STARTED:
            return ERROR_SERVICE_ALREADY_RUNNING;

        case STATUS_REDIRECTOR_HAS_OPEN_HANDLES:
            return ERROR_REDIRECTOR_HAS_OPEN_HANDLES;

        case STATUS_NO_MORE_FILES:
        case STATUS_NO_MORE_ENTRIES:
            return WN_NO_MORE_ENTRIES;

        case STATUS_MORE_ENTRIES:
            return WN_MORE_DATA;

        case STATUS_CONNECTION_IN_USE:
            return ERROR_DEVICE_IN_USE;

        case NWRDR_PASSWORD_HAS_EXPIRED:
            return NW_PASSWORD_HAS_EXPIRED;

        case STATUS_INVALID_DEVICE_REQUEST:
            return ERROR_CONNECTION_INVALID;

        default:
            return RtlNtStatusToDosError(NtStatus);
    }
}

DWORD
NwGetGraceLoginCount(
    LPWSTR  Server,
    LPWSTR  UserName,
    LPDWORD lpResult
    )
/*++

Routine Description:

    Get the number grace logins for a user.

Arguments:

    Server - the server to authenticate against

    UserName - the user account

Return Value:

    Returns the appropriate Win32 error.

--*/
{
    DWORD status ;
    HANDLE hConn ;
    CHAR UserNameO[NW_MAX_USERNAME_LEN+1] ;
    BYTE LoginControl[128] ;
    BYTE MoreFlags, PropFlags ;

    //
    // skip the backslashes if present
    //
    if (*Server == L'\\')
        Server += 2 ;

    //
    // attach to the NW server
    //
    if (status = NWAttachToFileServerW(Server,
                                       0,
                                       &hConn))
    {
        return status ;
    }

    //
    // convert unicode UserName to OEM, and then call the NCP
    //
    if ( !WideCharToMultiByte(CP_OEMCP,
                              0,
                              UserName,
                              -1,
                              UserNameO,
                              sizeof(UserNameO),
                              NULL,
                              NULL))
    {
        status = GetLastError() ;
    }
    else
    {
        status = NWReadPropertyValue( hConn,
                                      UserNameO,
                                      OT_USER,
                                      "LOGIN_CONTROL",
                                      1,
                                      LoginControl,
                                      &MoreFlags,
                                      &PropFlags) ;
    }

    //
    // dont need these anymore. if any error, bag out
    //
    (void) NWDetachFromFileServer(hConn) ;


    if (status == NO_ERROR)
        *lpResult = (DWORD) LoginControl[7] ;

    return status ;
}


WORD
NwParseNdsUncPath(
    IN OUT LPWSTR * Result,
    IN LPWSTR ContainerName,
    IN ULONG flag
)
/*++

Routine Description:

    This function is used to extract either the tree name, fully distinguished
    name path to an object, or object name, out of a complete NDS UNC path.

Arguments:

    Result - parsed result buffer.
    ContainerName - Complete NDS UNC path that is to be parsed.
    flag - Flag indicating operation to be performed:

         PARSE_NDS_GET_TREE_NAME
         PARSE_NDS_GET_PATH_NAME
         PARSE_NDS_GET_OBJECT_NAME


Return Value:

    Length of string in result buffer. If error occured, 0 is returned.

--*/ // NwParseNdsUncPath
{
    unsigned short length = 2;
    unsigned short totalLength = (USHORT) wcslen( ContainerName );

    if ( totalLength < 2 )
        return 0;

    //
    // First get length to indicate the character in the string that indicates the
    // "\" in between the tree name and the rest of the UNC path.
    //
    // Example:  \\<tree name>\<path to object>[\|.]<object>
    //                        ^
    //                        |
    //
    while ( length < totalLength && ContainerName[length] != L'\\' )
    {
        length++;
    }

    if ( flag == PARSE_NDS_GET_TREE_NAME )
    {
        *Result = (LPWSTR) ( ContainerName + 2 );

        return ( length - 2 ) * sizeof( WCHAR ); // Take off 2 for the two \\'s
    }

    if ( flag == PARSE_NDS_GET_PATH_NAME && length == totalLength )
    {
        *Result = ContainerName;

        return 0;
    }

    if ( flag == PARSE_NDS_GET_PATH_NAME )
    {
        *Result = ContainerName + length + 1;

        return ( totalLength - length - 1 ) * sizeof( WCHAR );
    }

    *Result = ContainerName + totalLength - 1;
    length = 1;

    while ( **Result != L'\\' )
    {
        *Result--;
        length++;
    }

    *Result++;
    length--;

    return length * sizeof( WCHAR );
}


DWORD
NwOpenAServer(
    PWCHAR pwszServName,
    PHANDLE ServerHandle,
    BOOL    fVerify
    )
/*++

Routine Description:

    This routine opens a handle to a server.

Arguments:

    ServerHandle - Receives an opened handle to the preferred or
        nearest server.

Return Value:

    NO_ERROR or reason for failure.

--*/
{
    UNICODE_STRING AServer;
    WCHAR wszName[sizeof(NW_RDR_NAME) + (48 * sizeof(WCHAR))];
    DWORD wLen;


    if(!pwszServName)
    {
        pwszServName = NW_RDR_PREFERRED_SERVER;
        RtlInitUnicodeString(&AServer, wszName);
    }
    else
    {
        wLen = wcslen(pwszServName);
        if(wLen > 47)
        {
            return(WSAEFAULT);
        }
        wcscpy(wszName, NW_RDR_NAME);
        wcscat(wszName, pwszServName);
        RtlInitUnicodeString(&AServer, wszName);
    }

    return RtlNtStatusToDosError(
               NwOpenHandle(&AServer, fVerify, ServerHandle)
               );

}


DWORD
NwOpenPreferredServer(
    PHANDLE ServerHandle
    )
/*++

Routine Description:

    This routine opens a handle to the preferred server.  If the
    preferred server has not been specified, a handle to the
    nearest server is opened instead.

Arguments:

    ServerHandle - Receives an opened handle to the preferred or
        nearest server.

Return Value:

    NO_ERROR or reason for failure.

--*/
{
    UNICODE_STRING PreferredServer;


    //
    // The NetWare redirector recognizes "*" to mean the preferred
    // or nearest server.
    //
    RtlInitUnicodeString(&PreferredServer, NW_RDR_PREFERRED_SERVER);

    return RtlNtStatusToDosError(
               NwOpenHandle(&PreferredServer, FALSE, ServerHandle)
               );

}


NTSTATUS
NwOpenHandle(
    IN PUNICODE_STRING ObjectName,
    IN BOOL ValidateFlag,
    OUT PHANDLE ObjectHandle
    )
/*++

Routine Description:

    This function opens a handle to \Device\Nwrdr\<ObjectName>.

Arguments:

    ObjectName - Supplies the name of the redirector object to open.

    ValidateFlag - Supplies a flag which if TRUE, opens the handle to
        the object by validating the default user account.

    ObjectHandle - Receives a pointer to the opened object handle.

Return Value:

    STATUS_SUCCESS or reason for failure.

--*/
{
    ACCESS_MASK DesiredAccess = SYNCHRONIZE;


    if (ValidateFlag) {

        //
        // The redirector only authenticates the default user credential
        // if the remote resource is opened with write access.
        //
        DesiredAccess |= FILE_WRITE_DATA;
    }


    *ObjectHandle = NULL;

    return NwCallNtOpenFile(
               ObjectHandle,
               DesiredAccess,
               ObjectName,
               FILE_SYNCHRONOUS_IO_NONALERT
               );

}


NTSTATUS
NwCallNtOpenFile(
    OUT PHANDLE ObjectHandle,
    IN ACCESS_MASK DesiredAccess,
    IN PUNICODE_STRING ObjectName,
    IN ULONG OpenOptions
    )
{

    NTSTATUS ntstatus;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES ObjectAttributes;



    InitializeObjectAttributes(
        &ObjectAttributes,
        ObjectName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
        );

    ntstatus = NtOpenFile(
                   ObjectHandle,
                   DesiredAccess,
                   &ObjectAttributes,
                   &IoStatusBlock,
                   FILE_SHARE_VALID_FLAGS,
                   OpenOptions
                   );

    if (!NT_ERROR(ntstatus) &&
        !NT_INFORMATION(ntstatus) &&
        !NT_WARNING(ntstatus))  {

        ntstatus = IoStatusBlock.Status;

    }

    return ntstatus;
}


BOOL
NwConvertToUnicode(
    OUT LPWSTR *UnicodeOut,
    IN LPSTR  OemIn
    )
/*++

Routine Description:

    This function converts the given OEM string to a Unicode string.
    The Unicode string is returned in a buffer allocated by this
    function and must be freed with LocalFree.

Arguments:

    UnicodeOut - Receives a pointer to the Unicode string.

    OemIn - This is a pointer to an ansi string that is to be converted.

Return Value:

    TRUE - The conversion was successful.

    FALSE - The conversion was unsuccessful.  In this case a buffer for
        the unicode string was not allocated.

--*/
{
    NTSTATUS ntstatus;
    DWORD BufSize;
    UNICODE_STRING UnicodeString;
    OEM_STRING OemString;


    //
    // Allocate a buffer for the unicode string.
    //

    BufSize = (strlen(OemIn) + 1) * sizeof(WCHAR);

    *UnicodeOut = LocalAlloc(LMEM_ZEROINIT, BufSize);

    if (*UnicodeOut == NULL) {
        KdPrint(("NWWORKSTATION: NwConvertToUnicode:LocalAlloc failed %lu\n",
                 GetLastError()));
        return FALSE;
    }

    //
    // Initialize the string structures
    //
    RtlInitAnsiString((PANSI_STRING) &OemString, OemIn);

    UnicodeString.Buffer = *UnicodeOut;
    UnicodeString.MaximumLength = (USHORT) BufSize;
    UnicodeString.Length = 0;

    //
    // Call the conversion function.
    //
    ntstatus = RtlOemStringToUnicodeString(
                   &UnicodeString,     // Destination
                   &OemString,         // Source
                   FALSE               // Allocate the destination
                   );

    if (ntstatus != STATUS_SUCCESS) {

        KdPrint(("NWWORKSTATION: NwConvertToUnicode: RtlOemStringToUnicodeString failure x%08lx\n",
                 ntstatus));

        (void) LocalFree((HLOCAL) *UnicodeOut);
        *UnicodeOut = NULL;
        return FALSE;
    }

    *UnicodeOut = UnicodeString.Buffer;

    return TRUE;

}