/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    msgnbios.c

Abstract:

    This file contains Routines used by the messenger to make netbios
    calls and to obtain the computer and user names.  
    The following functions are included:

        MsgInit_NetBios
        Msgsendncb
        MsgAddUserNames
        MsgAddAlreadyLoggedOnUserNames (HYDRA specific)

    
     NT_NOTE:
    
     We need some way to determine which NCB's have unanswered listens
     pending.  For these, we need to send an NCBCANCEL command via 
     another NetBios call.  The buffer will contain a pointer to the
     listening NCB.  Also, listens that are in the process of being
     serviced must either be hung up, or allowed to complete.
    
     All this is done during the MsgrShutdown.

Author:

    Dan Lafferty (danl)     27-Jun-1991

Environment:

    User Mode -Win32

Notes:

    NetBios3.0 is not a handle-based api.  Therefore, there is no open or
    close associated with it.  In order to shut down properly, the 
    messenger will have to hangup or complete listens that are being
    serviced.   And it will have to send a cancel NCB for each listen 
    that is pending.  

Revision History:

    08-Apr-1994     danl
        MsgAddUserNames: If call to NetWkstaUserEnum failed, this function
        was still attempting to free the buffer that was returned.
        Since no buffer is allocated in a failure case, the free mem
        call in the path is being removed.
    27-Jun-1991     danl
        ported from LM2.0

--*/


#include "msrv.h"

#include "msgdbg.h"     // MSG_LOG

#include <tstring.h>    // Unicode string macros
#include <icanon.h>     // I_NetNameCanonicalize
#include <netlib.h>     // UNUSED macro

#include <lmwksta.h>    // NetWorkstation API prototypes
#include <lmapibuf.h>   // NetApiBufferFree
#include <netdebug.h>   // NetpAssert, FORMAT_ equates.
#include "msgdata.h"
#include "apiutil.h"    // MsgMapNetError

    //
    // Note: we use the internal entrypoints to the apis because this
    // file is shared by the message apis, which cannot call the net
    // bios apis because of different required permissions.
    // 



NET_API_STATUS 
MsgInit_NetBios(
    VOID
    )

/*++

Routine Description:

    This function fills the global array called net_lana_num with the
    lan adapter numbers retrieved from A NetBios Enum call

    NOTE:  This assumes that space for the array is already set up.

    The LM2.0 version of this also filled an array of NetBios Handles.
    In LM2.0, the loopback driver was not included unless it was the only
    network installed.

Arguments:

    none

Return Value:

    TRUE  - No Error.
    FALSE - An error occured.

--*/

{
    DWORD                   count=0;
    NCB                     ncb;
    LANA_ENUM               lanaBuffer;
    unsigned char           i;  
    unsigned char           nbStatus;


    //
    // Find the number of networks by sending an enum request via Netbios.
    // 

    clearncb(&ncb);
    ncb.ncb_command = NCBENUM;          // Enumerate LANA nums (wait)
    ncb.ncb_buffer = (char FAR *)&lanaBuffer;
    ncb.ncb_length = sizeof(LANA_ENUM);

    nbStatus = Netbios(&ncb);

    if (nbStatus != NRC_GOODRET)
    {
        MSG_LOG(ERROR, "Netbios LanaEnum failed rc=%d\n",nbStatus);
        return MsgMapNetError(nbStatus);
    }

    //
    // Move the Adapter Numbers (lana) into the array that will contain them.
    //
    for (i=0; i < lanaBuffer.length; i++)
    {
        MSG_LOG(TRACE,"adapter %d",i);
        MSG_LOG(TRACE,"\b\b\b\b\b\b lananum= %d      \n", lanaBuffer.lana[i]);
        GETNETLANANUM(count) = lanaBuffer.lana[i];
        count++;

        //
        // Internal consistancy check.  Make sure the arrays are only
        // SD_NUMNETS long.
        //
        if (count > SD_NUMNETS())
        {
            MSG_LOG(ERROR,
                    "NumNets from NetBios greater than value from Wksta count=%d\n",
                    count);

            return NERR_WkstaInconsistentState;
        }
    }

    //
    // Internal consistancy check again. We better not have opened
    // more nets than the messenger thinks there are.
    // 

    if (count != SD_NUMNETS())
    {
        return NERR_WkstaInconsistentState;
    }

    return NERR_Success;

}

UCHAR
Msgsendncb(
    PNCB    NCB_ptr,
    DWORD   neti)

/*++

Routine Description:


    This function performs a DosDevIOCtl call to send an NCB to the
    net bios via a previously openned redirector and netbios handle.

Arguments:

    NCB_ptr - Points to the NCB to send to the net bios.
    neti - Network index.  Which netbios to submit it to?


Return Value:


    Error code from Net bios.

--*/
{
    //
    // NOTE:  The new Netbios call doesn't use any handles, so the neti
    //  info is not used.
    //

    UNUSED (neti);
    return (Netbios(NCB_ptr));

#ifdef remove
    return( NetBiosSubmit( NetBios_Hdl[neti], 0, (NCB far *) NCB_ptr));
#endif
}


VOID
MsgAddUserNames(
    VOID
    )

/*++

Routine Description:

   This function used to get it's information about the username and
   computername from the workstation service.  Now, in NT, the username
   is added when the user logs on.  It is not automatically added by the
   messenger.

Arguments:


    CompName - Pointer to buf for computer name. (must be NCBNAMSZ+1)
    CompNameSize - Size in bytes of the buffer to receive the name.

    UserName - Pointer to buffer for user name. (must be UNLEN+1)
    UserNameSize - Size in bytes of the buffer to receive the name.

Return Value:

    NERR_Success - Aways returned. (Names are returned as NUL strings).


--*/

{

    TCHAR               UserName[UNLEN+1];
    DWORD               UserNameSize = sizeof(UserName);
    DWORD               i;
    LPWKSTA_USER_INFO_0 userInfo0;
    DWORD               entriesRead;
    DWORD               totalEntries;
    NET_API_STATUS      status;
    
    *UserName = TEXT('\0');

    status = NetWkstaUserEnum( 
                NULL, 
                0,
                (LPBYTE *)&userInfo0,
                0xffffffff,             // PreferredMaximiumLength
                &entriesRead,
                &totalEntries,
                NULL);                  // resume handle

    if (status != NERR_Success) {
        MSG_LOG(ERROR,"GetWkstaNames:NetWkstaUserEnum FAILURE %X/n",status);
        return;
    }

    for (i=0; i<entriesRead; i++ ) {

        if (entriesRead == 0) {
            //
            // There are no users logged on at the time of this query.
            //
            MSG_LOG(TRACE,
                "GetWkstaNames:NetWkstaUserEnum entriesRead=%d\n",
                entriesRead);
        }
        
        if(userInfo0[i].wkui0_username != NULL) {
            status = I_NetNameCanonicalize(
                        NULL,
                        userInfo0[i].wkui0_username,
                        UserName,
                        UserNameSize,
                        NAMETYPE_USER,
                        0);
            if (status != NERR_Success) {
                MSG_LOG(ERROR,"I_NetNameCanonicalize failed %X\n",status);
            }
        }
                
        if( *UserName != TEXT('\0')) {        // Set up in GetWkstaNames */
            MSG_LOG(TRACE, "Calling MsgAddName\n",0);

            status = MsgAddName(UserName,0);

            if (status != NERR_Success) {
                MSG_LOG(
                    TRACE,
                    "MsgAddUserNames,MessageAddName FAILURE " FORMAT_API_STATUS
                    "\n",
                    status);
            }
        }
    }
    NetApiBufferFree(userInfo0);
}


VOID
MsgAddAlreadyLoggedOnUserNames(
    VOID
    )

/*++

Routine Description:

   This function is used to get information about the previously logged on usernames
   by calling WinStationEnumerate and WinStationQueryInformationW, instead of NetWkstaUserEnum.
   (same job as MsgAddUserNames, adapted for multi-user)

  Note: It could (should ?) be located elsewhere than in msgnbios. I kept it here just because
        MsgAddUserName itself was already here. (NicolasBD)

Arguments:

Return Value:

    NERR_Success - Always returned. 


--*/

{

    TCHAR               UserName[NCBNAMSZ+1];
    DWORD               UserNameSize = sizeof(UserName);
    UINT WdCount, i;
    PLOGONID pWd, pWdTmp;
    ULONG AmountRet;
    WINSTATIONINFORMATIONW QueryBuffer;
    NET_API_STATUS      status;
    
    *UserName = TEXT('\0');

    // Enumerate the Sessions

    if ( gpfnWinStationEnumerate( SERVERNAME_CURRENT, &pWd, &WdCount ) ) 
    {
        // Success; get all the previously logged on user names and session ids

        pWdTmp = pWd;
        for( i=0; i < WdCount; i++ ) {

            if( ((pWdTmp->State == State_Connected) ||
                 (pWdTmp->State == State_Active) ||
                 (pWdTmp->State == State_Disconnected)))
            {
                if( !gpfnWinStationQueryInformation( SERVERNAME_CURRENT,
                                                     pWdTmp->LogonId,
                                                      WinStationInformation,
                                                      &QueryBuffer,
                                                      sizeof(QueryBuffer),
                                                      &AmountRet ) )
                {
                    // Error
                    MSG_LOG(ERROR, "MsgAddAlreadyLoggedOnUserNames: Error in QueryInfo %d\n",GetLastError());
                }
                else
                {
                    if (QueryBuffer.UserName != NULL) 
                    {
                        MSG_LOG(TRACE,"MsgAddAlreadyLoggedOnUserNames: calling I_NetNameCanonicalize for %ws\n",QueryBuffer.UserName);

                        status = I_NetNameCanonicalize(
                                    NULL,
                                    QueryBuffer.UserName,
                                    UserName,
                                    UserNameSize,
                                    NAMETYPE_USER,
                                    0);
                        if (status != NERR_Success) 
                        {
                            MSG_LOG(ERROR,"I_NetNameCanonicalize failed %X\n",status);
                        }
                    }
                
                    if( *UserName != TEXT('\0')) 
                    {   
	                    MSG_LOG(TRACE,"MsgAddAlreadyLoggedOnUserNames: Calling MsgAddName for Session %x \n", pWdTmp->LogonId);
                        status = MsgAddName(UserName, pWdTmp->LogonId);

                        if (status != NERR_Success) 
                        {
                            MSG_LOG(TRACE, "MsgAddAlreadyLoggedOnUserNames,MessageAddName FAILURE " FORMAT_API_STATUS "\n", status);
                        }
                    }
                }
            }

            pWdTmp++;
        }

        // Free enumeration memory

        gpfnWinStationFreeMemory(pWd);

    }
    else
    {
        MSG_LOG (ERROR, "MsgAddAlreadyLoggedOnUserNames: WinStationEnumerate failed, error = %d:\n",GetLastError());
    }

    return;
}