/*++

Copyright (c) 1991-1992  Microsoft Corporation

Module Name:

    apiutil.c

Abstract:

    Contains functions used by the Messenger API.  This file contains the
    following functions:

        MsgIsValidMsgName
        MsgMapNetError
        MsgLookupName
        message_sec_check
        MsgGatherInfo
        MsgUnformatName

        MsgLookupNameForThisSession 
        MsgIsSessionInList (HYDRA specific)
        MsgAddSessionInList (HYDRA specific)
        MsgRemoveSessionFromList (HYDRA specific)

Author:

    Dan Lafferty (danl)     22-Jul-1991

Environment:

    User Mode -Win32

Notes:

    These functions were ported from LM2.0.  This file contains functions
    from several LM2.0 files.  Not all functions were used in this file
    since some were made obsolete by the NT Service Model.
    The following LM2.0 files were incorportated into this single file:
        
        msgutils.c
        msgutil2.c
        dupname.c
        netname.c
    
Revision History:

    22-Jul-1991     danl
        ported from LM2.0

--*/

//
// Includes
// 


#include "msrv.h"
#include <tstring.h>    // Unicode string macros
#include <lmwksta.h>
#include <lmmsg.h>

#include <smbtypes.h>   // needed for smb.h
#include <smb.h>        // Server Message Block definitions

#include <icanon.h>     // I_NetNameValidate
#include <netlib.h>     // NetpCopyStringToBuffer

#include "msgdbg.h"     // MSG_LOG
#include "heap.h"
#include "msgdata.h"
#include "apiutil.h"


//
// Table of NetBios mappings to Net Errors.
//
    DWORD   const mpnetmes[] = 
    {
    0x23,                       // 00 Number of messages
    NERR_NetworkError,          // 01 NRC_BUFLEN -> invalid length
    0xffffffff,                 // 02 NRC_BFULL , not expected
    NERR_NetworkError,          // 03 NRC_ILLCMD -> invalid command
    0xffffffff,                 // 04 not defined
    NERR_NetworkError,          // 05 NRC_CMDTMO -> network busy
    NERR_NetworkError,          // 06 NRC_INCOMP -> messgae incomplete
    0xffffffff,                 // 07 NRC_BADDR , not expected
    NERR_NetworkError,          // 08 NRC_SNUMOUT -> bad session
    NERR_NoNetworkResource,     // 09 NRC_NORES -> network busy
    NERR_NetworkError,          // 0a NRC_SCLOSED -> session closed
    NERR_NetworkError,          // 0b NRC_CMDCAN -> command cancelled
    0xffffffff,                 // 0c NRC_DMAFAIL, unexpected
    NERR_AlreadyExists,         // 0d NRC_DUPNAME -> already exists 
    NERR_TooManyNames,          // 0e NRC_NAMTFUL -> too many names
    NERR_DeleteLater,           // 0f NRC_ACTSES -> delete later
    0xffffffff,                 // 10 NRC_INVALID , unexpected
    NERR_NetworkError,          // 11 NRC_LOCTFUL -> too many sessions
    ERROR_REM_NOT_LIST,         // 12 NRC_REMTFUL -> remote not listening*/
    NERR_NetworkError,          // 13 NRC_ILLNN -> bad name
    NERR_NameNotFound,          // 14 NRC_NOCALL -> name not found
    ERROR_INVALID_PARAMETER,    // 15 NRC_NOWILD -> bad parameter
    NERR_DuplicateName,         // 16 NRC_INUSE -> name in use, retry
    ERROR_INVALID_PARAMETER,    // 17 NRC_NAMERR -> bad parameter
    NERR_NetworkError,          // 18 NRC_SABORT -> session ended
    NERR_DuplicateName,         // 19 NRC_NAMCONF -> duplicate name
    0xffffffff,                 // 1a not defined
    0xffffffff,                 // 1b not defined
    0xffffffff,                 // 1c not defined
    0xffffffff,                 // 1d not defined
    0xffffffff,                 // 1e not defined
    0xffffffff,                 // 1f not defined
    0xffffffff,                 // 20 not defined
    NERR_NetworkError,          // 21 NRC_IFBUSY -> network busy
    NERR_NetworkError,          // 22 NRC_TOOMANY -> retry later
    NERR_NetworkError           // 23 NRC_BRIDGE -> bridge error
    };


DWORD        
MsgIsValidMsgName(
    IN LPTSTR  name
    )

/*++

Routine Description:

    Check for a valid messaging name.
    This function checks for the validity of a messaging name.


Arguments:

    name - pointer to name to validate.


Return Value:

    Error code from I_NetNameValidate


--*/

{
    TCHAR   namebuf[NCBNAMSZ];
    DWORD   err_code;

    //
    // Message names cannot be larger than (NCBNAMSZ - 1) characters
    //
    if (STRLEN(name) > (NCBNAMSZ - 1))
    {
        return ERROR_INVALID_PARAMETER;
    }
    
    STRCPY(namebuf, name);

    err_code = I_NetNameValidate(NULL, namebuf, NAMETYPE_COMPUTER, 0L);

    if (err_code != 0)
    {
        return err_code;
    }
    
    //
    // Any name beginning with a * must be rejected as the message 
    // server relies on being able to ASTAT the name, and an ASTAT
    // name commencing with a * means ASTAT the local card.
    // 
    if(namebuf[0] == TEXT('*'))
    {
        return ERROR_INVALID_PARAMETER;
    }

    return NERR_Success;
}


DWORD
MsgMapNetError(
    IN  UCHAR   Code        // Error code
    )

/*++

Routine Description:

    Map NetBios Error code to a message number

Arguments:

    code - Error code from NetBios (can be 0)

Return Value:

    Message code as defined in msgs.h

--*/
{
    DWORD   dwCode;

    dwCode = 0 | (UCHAR)Code;
                                            
    if( dwCode == 0) {
        return(NERR_Success);               // Special case
    }

    if((dwCode > 0) && (dwCode < mpnetmes[0])) {
        return(mpnetmes[dwCode]);
    }

    return (NERR_NetworkError);             // Can't map it!
}


DWORD
MsgLookupName(
    IN DWORD    net,        // The network card to search
    IN LPSTR    name        // Formatted name  (Non-unicode)
    )

/*++

Routine Description:

    This function looks up a formatted name in the name table in the
    Message Server's shared data area.
 
    This function looks the given name up in the name table in the shared
    data area.  In order to match the given name, the first NCBNAMLEN - 1
    characters of the name in the name table must be identical to the same
    characters in the given name, and the name in the name table must not be
    marked as deleted.  This function assumes that the shared data area is
    accessible and that the global variable, dataPtr, is valid.

Arguments:

    name - pointer to formatted name


Return Value:

    DWORD - index into table if found, -1 otherwise

--*/

{
    DWORD   i;                              // Index

    for(i = 0; i < NCBMAX(net); ++i) {           // Loop to search for name

        if( !memcmp( name, SD_NAMES(net,i), NCBNAMSZ - 1) &&
            !(SD_NAMEFLAGS(net,i) & NFDEL) ) {

            //
            // Return index if match found
            //
            return(i);
        }
    }                                       
    return(0xffffffff);                     // No match
}

//	For HYDRA, we want to make sure that the name exists for THIS client session.
DWORD
MsgLookupNameForThisSession(
    IN DWORD    net,        // The network card to search
    IN LPSTR    name,        // Formatted name to loook for (Non-unicode)
	IN ULONG	SessionId	 // Session Id to look for
    )
/*++

Routine Description:

    Same as MsgLookupName except that we care about the session Id.
    This function looks up a formatted name in the name table in the
    Message Server's shared data area. The name found must have the 
    requested SessionId in its session list to be considered as OK.

Arguments:

    name - pointer to formatted name
    SessionId - the requested Session Id


Return Value:

    DWORD - index into table if found, -1 otherwise

--*/

{
    DWORD   i;                              // Index
    DWORD   dwMsgrState;                    // messanger state

    if (!g_IsTerminalServer)        //  regular NT case
    {
        //
        //  if we are not on HYDRA, forget the SessionId
        //
        return MsgLookupName(net, name);
    }
    else            //  HYDRA case
    {
        //
        // dont try to access table if messanger stop is pending,
        //  we may not have GlobalData available
        //
        dwMsgrState = GetMsgrState();
        if (RUNNING == dwMsgrState)
        {
            for(i = 0; i < NCBMAX(net); ++i) {           // Loop to search for name

                if( !memcmp( name, SD_NAMES(net,i), NCBNAMSZ - 1) &&
                    !(SD_NAMEFLAGS(net,i) & NFDEL) &&
                    (MsgIsSessionInList(&(SD_SIDLIST(net,i)), SessionId ))
                    ) {
    			    return (i);
                }
            }
        }
        return(0xffffffff);                     // No match
    }
}


// message_sec_check
//
//    A common routine to check caller priv/auth against that
//  required to call the message apis.
//
// 

NET_API_STATUS
message_sec_check(VOID)
{
#ifdef later
    //
    // API security check. This call can be called by anyone locally,
    // but only by admins in the remote case.
    
    I_SecSyncSet(SECSYNC_READER);

    if ( ( clevel == ACCESS_REMOTE ) &&
         ( callinf != NULL ) &&
         ( CALLER_PRIV(callinf) != USER_PRIV_ADMIN ) )
    {
        I_SecSyncClear(SECSYNC_READER);
        return(ERROR_ACCESS_DENIED);
    }
    I_SecSyncClear(SECSYNC_READER);
#endif
    return (NERR_Success);
}                            


NET_API_STATUS 
MsgGatherInfo (
    IN      DWORD   Level,
    IN      LPSTR   FormattedName,
    IN OUT  LPBYTE  *InfoBufPtr,
    IN OUT  LPBYTE  *StringBufPtr
    )

/*++

Routine Description:



Arguments:

    Level - Indicates the  level of information that is being returned.

    FormattedName - This is a name that messages are received by.  This
        name is formated for NCB transactions.  Therefore, it is made
        up of ANSI characters that are space padded to fill out a packet
        of NCBNAMSZ characters.  The last character is always a 03 
        (indicating a non-forwarded name).
     
    InfoBufPtr - On input, this is a pointer to a pointer to where the
        messenger information is to be placed.  On successful return, this
        location contains a pointer to the location where the next
        information will be placed (on the next call to this function).

    StringBufPtr -  On input, thisis a pointer to a pointer to where the
        NUL terminated name string for that info record is to be placed.
        On successful return, this location contains a pointer to the
        location where the next set of strings will be placed (on the
        next call to this function).

Return Value:

    NERR_Success - The information was successfully gathered and placed
        in the info buffer.

    NERR_Internal_Error - The Formatted Name could not be correctly
        translated into a meaningful Unicode Name.

    ERROR_INVALID_LEVEL - An illegal info level was passed in.

    ERROR_NOT_ENOUGH_MEMORY - Not enough room to store gathered information.

--*/
{
    NET_API_STATUS  status;
    BOOL            bStatus;
    PCHAR           fixedDataEnd;   // pointer to free space from top of buffer.
    LPMSG_INFO_0    infoBuf0;
    LPMSG_INFO_1    infoBuf1;
    TCHAR           unicodeName[NCBNAMSZ];

    //
    // Convert the name to Unicode
    //
    status = MsgUnformatName(unicodeName, FormattedName);
    if (status != NERR_Success) {
        return(status);
    }
    
    switch (Level) {
    case LEVEL_0:
        infoBuf0 = (LPMSG_INFO_0)*InfoBufPtr;
        fixedDataEnd = (PCHAR)infoBuf0 + sizeof(MSG_INFO_0);

        if( fixedDataEnd >= *StringBufPtr) {
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        bStatus = NetpCopyStringToBuffer (
                    unicodeName,                // The String
                    STRLEN(unicodeName),        // StringLength
                    fixedDataEnd,               // FixedDataEnd
                    (PVOID)StringBufPtr,        // EndOfVariableData
                    &infoBuf0->msgi0_name);     // VariableDataPointer

        if (bStatus == FALSE) {
            MSG_LOG(TRACE,"MsgGatherInfo(level0): Not enough room\n",0);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }
        *InfoBufPtr = (LPBYTE)fixedDataEnd;
        break;

    case LEVEL_1:
        infoBuf1 = (LPMSG_INFO_1)*InfoBufPtr;

        fixedDataEnd = (PCHAR)infoBuf1 + sizeof(MSG_INFO_1);
        if( fixedDataEnd >= *StringBufPtr) {
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        bStatus = NetpCopyStringToBuffer (
                    unicodeName,                // The String
                    STRLEN(unicodeName),        // StringLength
                    fixedDataEnd,               // FixedDataEnd
                    (PVOID)StringBufPtr,        // EndOfVariableData
                    &infoBuf1->msgi1_name);     // VariableDataPointer

        if (bStatus == FALSE) {
            MSG_LOG(TRACE,"MsgGatherInfo(level1): Not enough room\n",0);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        //
        // Set all the forward information to NULL since forwarding
        // is not supported.
        //
        infoBuf1->msgi1_forward_flag = 0;
        infoBuf1->msgi1_forward = NULL;
        
        *InfoBufPtr = (LPBYTE)fixedDataEnd;
        break;

    default:
        MSG_LOG(TRACE,"MsgGatherInfo Invalid level\n",0);
        return(ERROR_INVALID_LEVEL);
        break;
    }

    return(NERR_Success);

}


NET_API_STATUS
MsgUnformatName(
    OUT LPTSTR  UnicodeName,
    IN  LPSTR   FormattedName
    )

/*++

Routine Description:

    This routine creates a Unicode NUL-Terminated version of a NetBios
    Formatted name.

Arguments:

    UnicodeName - This is a pointer to a location where the un-formatted
        NUL terminated Unicode Name is to be copied.

    FormattedName - This is a pointer to an NCB formatted name.  This
        name always contains NCBNAMSZ characters of which the last character
        is a code used for a forward/non-forward flag.  These strings
        are space padded.
        

Return Value:

    NERR_Success - The operation was successful.

    NERR_Internal - The operation was unsuccessful.

--*/
{
    UNICODE_STRING  unicodeString;
    OEM_STRING     ansiString;
    NTSTATUS        ntStatus;
    int             i;

    //
    // Translate the ansi string in the name table to a unicode name
    //
#ifdef UNICODE
    unicodeString.Length = (NCBNAMSZ -1) * sizeof(WCHAR);
    unicodeString.MaximumLength = NCBNAMSZ * sizeof(WCHAR);
    unicodeString.Buffer = (LPWSTR)UnicodeName;

    ansiString.Length = NCBNAMSZ-1;
    ansiString.MaximumLength = NCBNAMSZ;
    ansiString.Buffer = FormattedName;

    ntStatus = RtlOemStringToUnicodeString(
                &unicodeString,      // Destination
                &ansiString,         // Source
                FALSE);              // Don't allocate the destination.

    if (!NT_SUCCESS(ntStatus)) {
        MSG_LOG(ERROR,
            "UnformatName:RtlOemStringToUnicodeString Failed rc=%X\n",
            ntStatus);
        //
        // Indicate a failure
        //
        return(NERR_InternalError);  
    }
#else
    UNUSED (ntStatus);
    UNUSED (ansiString);
    UNUSED (unicodeString);
    strncpy(UnicodeName, FormattedName, NCBNAMSZ-1);
#endif

    //
    // Remove excess Space characters starting at the back (skipping
    // the 03 flag character.
    //
    i = NCBNAMSZ-2;

    while ( UnicodeName[i] == TEXT(' ')) {

        UnicodeName[i--] = TEXT('\0');

        if (i < 0) {
            MSG_LOG(ERROR,
                "UnformatName:Nothing but space characters\n",0);
            return(NERR_InternalError);
        }
    }
    return(NERR_Success);
}


BOOL
MsgIsSessionInList(
				   IN PLIST_ENTRY SessionIdList,
				   IN ULONG SessionId
				   )
{
	BOOL		bRet = FALSE;

	PLIST_ENTRY				pList = SessionIdList;
	PMSG_SESSION_ID_ITEM	pItem;

	while (pList->Flink != SessionIdList)		// loop until we find it (or the end of the list)
	{
        pList = pList->Flink;
		pItem = CONTAINING_RECORD(pList, MSG_SESSION_ID_ITEM, List);
		if ( (pItem->SessionId == SessionId) || (pItem->SessionId == EVERYBODY_SESSION_ID) )
		{
			bRet = TRUE;	// we found it !
			break;
		}
	}

    return bRet;
}


VOID
MsgRemoveSessionFromList(
					  IN PLIST_ENTRY SessionIdList,
					  ULONG	SessionId
					  )
{
	PLIST_ENTRY				pList = SessionIdList;
	PMSG_SESSION_ID_ITEM	pItem;

	while (pList->Flink != SessionIdList)		// loop until we find it (or the end of the list)
	{
        pList = pList->Flink;  
		pItem = CONTAINING_RECORD(pList, MSG_SESSION_ID_ITEM, List);
		if (pItem->SessionId == SessionId)
		{
			// we found it. Let's remove it
			RemoveEntryList(pList);

			//Free the memory
			LocalFree(pItem);

			break;
		}
	}
}


BOOL
MsgAddSessionInList(
					 IN PLIST_ENTRY SessionIdList,
					 ULONG	SessionId
					 )
{
	BOOL		bRet;
	PMSG_SESSION_ID_ITEM	pItem;

	// allocate a new item
	pItem = (PMSG_SESSION_ID_ITEM) LocalAlloc(LMEM_ZEROINIT,sizeof(MSG_SESSION_ID_ITEM));

	if (pItem == NULL)	// If this happens, we really have big problems...
	{
		MSG_LOG(ERROR,"MsgAddSessionInList:  Unable to allocate memory\n",0);
		bRet = FALSE;
    }
	else	//	OK
	{
		bRet = TRUE;

		// initialize the item
		pItem->SessionId = SessionId;

		// insert the item in the list
		InsertTailList(SessionIdList, &pItem->List);
	}
	return bRet;
}