/*++

Copyright (C) 1999 Microsoft Corporation

--*/

#include "precomp.h"


#define WCHARTONUM(wchr) (iswalpha(wchr)?(towlower(wchr)-L'a')+10:wchr-L'0')

UCHAR  StringToHexA(IN LPCWSTR pwcString)
{
    UCHAR   ch = (CHAR)0x00;

    if( pwcString is NULL )
        return ch;

    while(*pwcString != L'\0')
    {
        ch <<= 4;
        ch |= WCHARTONUM(*pwcString);
        pwcString++;
    }

   return ch;
}



ULONG LA_TableSize = 0;

VOID
FreeLATable(ULONG TableSize)
{
    DWORD i = 0;

    if( IsBadReadPtr((void *)LA_Table, TableSize) is FALSE && 
        TableSize > 0 )
    {
        for( i=0; i<TableSize; i++ )
        {
            if( IsBadStringPtr(LA_Table[i], 20*sizeof(WCHAR)) is FALSE )
            {
                WinsFreeMemory((PVOID)LA_Table[i]);
                LA_Table[i] = NULL;
            }
        }

        WinsFreeMemory((PVOID)LA_Table);
        LA_Table = NULL;
    }
}

VOID
FreeSOTable(ULONG TableSize)
{
    DWORD i = 0;

    if( SO_Table )
    {
        for( i=0; i<TableSize; i++ )
        {
            if( SO_Table[i] )
            {
                WinsFreeMemory((PVOID)SO_Table[i]);
                SO_Table[i] = NULL;
            }
        }

        WinsFreeMemory((PVOID)SO_Table);
        SO_Table = NULL;
    }
}

VOID
DumpSOTable(
    IN DWORD MasterOwners,
    IN BOOL     fFile,
    IN FILE *   pFile
    )
{
    ULONG   i;
    ULONG   j;

    DisplayMessage(g_hModule, 
                   MSG_WINS_SOTABLE_HEADER);
    if( fFile is TRUE )
    {
        DumpMessage(g_hModule,
                    pFile,
                    FMSG_WINS_SOTABLE_HEADER);
    }


    for (i = 0; i < MasterOwners; i++) 
    {
        DisplayMessage(g_hModule,
                       MSG_WINS_MASTEROWNER_INDEX,
                       i);
        if( fFile )
        {
            DumpMessage(g_hModule,
                        pFile,
                        FMSG_WINS_MASTEROWNER_INDEX,
                        i);
        }
    }

    DisplayMessage(g_hModule,
                   WINS_FORMAT_LINE);
    if( fFile )
    {
        DumpMessage(g_hModule,
                    pFile,
                    WINS_FORMAT_LINE);
    }

    for (i = 0; i < MasterOwners; i++) 
    {
        DisplayMessage(g_hModule,
                       MSG_WINS_MASTEROWNER_INDEX1,
                       i);
        if( fFile )
        {
            DumpMessage(g_hModule,
                        pFile,
                        FMSG_WINS_MASTEROWNER_INDEX1,
                        i);
        }
        for (j = 0; j < MasterOwners; j++) 
        {
            DisplayMessage(g_hModule,
                           MSG_WINS_MASTEROWNER_INDEX,
                           SO_Table[i][j]);
            if( fFile )
            {
                DumpMessage(g_hModule,
                            pFile,
                            FMSG_WINS_MASTEROWNER_INDEX,
                            SO_Table[i][j]);
            }
        }

        DisplayMessage(g_hModule,
                       WINS_FORMAT_LINE);
        if( fFile )
        {
            DumpMessage(g_hModule,
                        pFile,
                        WINS_FORMAT_LINE);
        }
    }

    
    DisplayMessage(g_hModule,
                   MSG_WINS_MAP_SOURCE);

    if( fFile )
    {
        DumpMessage(g_hModule,
                    pFile,
                    FMSG_WINS_MAP_SOURCE);
    }

    DumpLATable(MasterOwners, fFile, pFile);
}

VOID
DumpLATable(
    IN DWORD MasterOwners,
    IN BOOL     fFile,
    IN FILE  *  pFile
    )
{
    ULONG   i;
    ULONG   j;

    
    DisplayMessage(g_hModule,
                   MSG_WINS_INDEXTOIP_TABLE);

    if( fFile )
    {
        DumpMessage(g_hModule,
                    pFile,
                    FMSG_WINS_INDEXTOIP_TABLE);

    }
    for (i = 0; i < MasterOwners; i++) 
    {
        if (LA_Table[i][0] == '0') 
        {
            break;
        }
    
        DisplayMessage(g_hModule,
                       MSG_WINS_INDEXTOIP_ENTRY,
                       i,
                       LA_Table[i]);

        if( pFile )
        {
            DumpMessage(g_hModule,
                        pFile,
                        FMSG_WINS_INDEXTOIP_ENTRY,
                        i,
                        LA_Table[i]);
        }

    }
    
    DisplayMessage(g_hModule,
                   WINS_FORMAT_LINE);
    if( fFile )
    {
        DumpMessage(g_hModule,
                    pFile,
                    WINS_FORMAT_LINE);
    }
}

LONG
IPToIndex(
    IN  LPWSTR  IpAddr,
    DWORD   NoOfOwners
    )
{
    ULONG   i=0;
    WCHAR **pTempLA = NULL;
    //
    // Get the Row #
    //
    for ( i = 0; i < NoOfOwners; i++) {
        if (wcscmp(LA_Table[i], IpAddr) == 0) {
            return i;
        }
        //
        // The first NULL entry indicates end
        //
        if (LA_Table[i][0] is L'0') {
            break;
        }
    }

    //
    // Entry not found - add
    //
    
    wcscpy(LA_Table[i], IpAddr);
    
    LA_TableSize = i+1;
    return i;
}

//
// Check if the diagonal elements are the max in their cols.
//
VOID
CheckSOTableConsistency(
    DWORD   MasterOwners
    )
{
    ULONG   i;
    ULONG   j;
    BOOLEAN fProblem = FALSE;

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

        //
        // Is the diagonal element at i the largest in its column?
        //
        for (j = 0; j < MasterOwners; j++) 
        {
            if (i == j) 
            {
                continue;
            }

            //
            // Compare only non-zero values
            //
            if (SO_Table[i][i].QuadPart &&
                SO_Table[j][i].QuadPart &&
                (SO_Table[i][i].QuadPart < SO_Table[j][i].QuadPart))
            {
                 
                DisplayMessage(g_hModule, EMSG_WINS_VERSION_HIGHER, LA_Table[j], LA_Table[i]);
                fProblem = TRUE;
            }
        }
    }

    if ( fProblem is FALSE ) 
    {
        DisplayMessage(g_hModule, EMSG_WINS_VERSION_CORRECT);
    }
}

DWORD
InitLATable(
    PWINSINTF_ADD_VERS_MAP_T  pAddVersMaps,
    DWORD   MasterOwners,           // 0 first time
    DWORD   NoOfOwners
    )
{
    ULONG   i, j, k;

    if( LA_Table is NULL )
    {
        LA_Table = WinsAllocateMemory(MAX_WINS*sizeof(LPWSTR));
        if( LA_Table is NULL )
        {
             return 0;
        }    

        for( i=0; i< MAX_WINS; i++ )
        {
            LA_Table[i] = WinsAllocateMemory(20*sizeof(WCHAR));
            if( LA_Table[i] is NULL )
            {
                FreeLATable(i);
                return 0;
            }

        }
    }

    if (MasterOwners == 0) 
    {
        //
        // first time - init the LA table
        //
        for (i = 0; i < NoOfOwners; i++, pAddVersMaps++) 
        {
            struct in_addr            InAddr;

            LPWSTR  pwsz = IpAddressToString(pAddVersMaps->Add.IPAdd);
            if( pwsz is NULL )
            {
                FreeLATable(MAX_WINS);
                return 0;
            }

            wcscpy(LA_Table[i], pwsz);
            
            WinsFreeMemory(pwsz);
            pwsz = NULL;

        }
    } 
    else 
    {
        //
        // More came in this time - add them to the LA table after the others
        //
        for (i = 0; i < NoOfOwners; i++, pAddVersMaps++) 
        {
            LPWSTR pwszIp = IpAddressToString(pAddVersMaps->Add.IPAdd);

            if( pwszIp is NULL )
            {
                FreeLATable(MAX_WINS);
                return 0;
            }
            //
            // If this entry is not in the LA table, insert
            //
            for (j = 0; j < MasterOwners; j++) 
            {
                if (wcscmp(LA_Table[j], pwszIp) is 0 ) 
                {
                    WinsFreeMemory(pwszIp);
                    pwszIp = NULL;
                    break;
                }
            }

            if (j == MasterOwners) 
            {
                //
                // Insert
                //


                wcscpy(LA_Table[MasterOwners], pwszIp);
                MasterOwners++;
            }

            if( pwszIp )
            {
                WinsFreeMemory(pwszIp);
                pwszIp = NULL;
            }
        }
    }
    
    if( SO_Table is NULL )
    {
        SO_Table = WinsAllocateMemory((MAX_WINS+1)*sizeof(LARGE_INTEGER *));
        if (SO_Table == NULL)
        {
            FreeLATable(MAX_WINS);
            return 0;
        }
    
        for( i=0; i<MAX_WINS+1; i++ )
        {
            SO_Table[i] = WinsAllocateMemory((MAX_WINS+1)*sizeof(LARGE_INTEGER));
            if( SO_Table[i] is NULL )
            {
                FreeLATable(MAX_WINS);
                FreeSOTable(MAX_WINS+1);
                return 0;
            }
        }
    }

    LA_TableSize = NoOfOwners;
    return MasterOwners;
}



VOID
AddSOTableEntry (
    IN  LPWSTR  IpAddr,
    PWINSINTF_ADD_VERS_MAP_T  pMasterMaps,
    DWORD   NoOfOwners,
    DWORD   MasterOwners
    )
{
    ULONG   i;
    LONG   Row;
    struct in_addr            InAddr;

    Row = IPToIndex(IpAddr, MasterOwners);

    //
    // Fill the row
    //
    for ( i = 0; i < NoOfOwners; i++, pMasterMaps++) 
    {
        LONG    col;
        LPTSTR  pstr;

        InAddr.s_addr = htonl(pMasterMaps->Add.IPAdd);

        pstr = IpAddressToString(pMasterMaps->Add.IPAdd);
        if (pstr == NULL)
            break;

        col = IPToIndex(pstr, MasterOwners);
        //
        // Place only a non-deleted entry
        //
        if (!((pMasterMaps->VersNo.HighPart == MAXLONG) &&
              (pMasterMaps->VersNo.LowPart == MAXULONG))) {

            //
            // Also if the entry above us was 0, write 0 there so as to make the fail case stand out
            //
            if (Row && SO_Table[Row-1][col].QuadPart == 0) 
            {
                SO_Table[Row][col].QuadPart = 0;
            } 
            else 
            {
                SO_Table[Row][col] = pMasterMaps->VersNo;
            }

        }
    }
}

VOID
RemoveFromSOTable(
    IN  LPWSTR  IpAddr,
    IN  DWORD   MasterOwners
    )
{
    ULONG   i;
    LONG   Row;
    struct in_addr            InAddr;

    Row = IPToIndex(IpAddr, MasterOwners);

    //
    // Mark the row and col as down (0's)
    //
    for (i = 0; i < MasterOwners; i++) 
    {
        SO_Table[Row][i].QuadPart = SO_Table[i][Row].QuadPart = 0;
    }
}


//
// Get the <owner address> - <version #> [OV table] mapping tables from each WINS server on the net and check for inconsistencies.
//
VOID
CheckVersionNumbers( 
                    IN  LPCSTR  pStartIp,
                    IN  BOOL    fFile,
                    OUT FILE *  pFile
                   )
{
    DWORD                     Status = NO_ERROR;
    ULONG                     i, k;
    PWINSINTF_ADD_VERS_MAP_T  pAddVersMaps;
    PWINSINTF_ADD_VERS_MAP_T  pMasterMaps;  // master OV maps used to place into the OV table
    DWORD                     NoOfOwners=0;
    DWORD                     MasterOwners=0;
    struct in_addr            InAddr;
    WINSINTF_RESULTS_NEW_T    ResultsN;
    DWORD                     ret;
    handle_t                  hBindTemp = g_hBind;
    WINSINTF_BIND_DATA_T      BindDataTemp = g_BindData;
    LPWSTR                    wszStartIp = NULL;
    
    if( pStartIp is NULL )
        return;
    
    wszStartIp = WinsOemToUnicode(pStartIp, NULL);  
    
    if( wszStartIp is NULL )
    {
        DisplayMessage(g_hModule,
                       EMSG_WINS_OUT_OF_MEMORY);
        return;
    }

    //
    // Get the OV table from this server
    //
    if (NO_ERROR isnot (Status = GetStatus(TRUE, &ResultsN, TRUE, FALSE, pStartIp)) )
    {
        DisplayErrorMessage(EMSG_SRVR_CHECK_VERSION,
                            Status);
          
        WinsFreeMemory(wszStartIp);
        wszStartIp = NULL;
        return;
    }
    else
    {
        DisplayMessage(g_hModule,
                       MSG_WINS_GETSTATUS_SUCCESS);

        DisplayMessage(g_hModule,
                       WINS_FORMAT_LINE);
    }

    MasterOwners = NoOfOwners = ResultsN.NoOfOwners;

    pMasterMaps = pAddVersMaps = ResultsN.pAddVersMaps;

    ret = InitLATable(pAddVersMaps, 0, NoOfOwners);
    
    if( LA_Table is NULL )
    {
        DisplayMessage(g_hModule,
                       EMSG_WINS_OUT_OF_MEMORY);
        WinsFreeMemory(wszStartIp);
        wszStartIp = NULL;
        return;
    }

    if( SO_Table is NULL )
    {
        DisplayMessage(g_hModule,
                       EMSG_WINS_OUT_OF_MEMORY);
        FreeLATable(MAX_WINS);    
        WinsFreeMemory(wszStartIp);
        wszStartIp = NULL;
        return;
    }

    AddSOTableEntry(wszStartIp, pMasterMaps, NoOfOwners, MasterOwners);


    if( ResultsN.pAddVersMaps )
    {
        WinsFreeMem(ResultsN.pAddVersMaps);
        ResultsN.pAddVersMaps = NULL;
    }



    //
    // For each server X (other than Start addr) in the LA table:
    //
    for ( i = 0; i < MasterOwners; i++) 
    {
        LPSTR   pszName = NULL;

        if( wcscmp(LA_Table[i], wszStartIp) is 0 )
        {
            continue;
        }

        //
        // Get X's OV table
        //
        pszName = WinsUnicodeToOem(LA_Table[i], NULL);

        if( pszName is NULL )
        {
            DisplayMessage(g_hModule,
                           EMSG_WINS_OUT_OF_MEMORY);
            if( SO_Table )
            {
                FreeSOTable(MAX_WINS+1);
            }
            if( wszStartIp )
            {
                WinsFreeMemory(wszStartIp);
                wszStartIp = NULL;
            }
            
            if( LA_Table )
            {
                FreeLATable(MAX_WINS);
            }
            return;

        }

        if( NO_ERROR isnot (Status = GetStatus(TRUE, &ResultsN, TRUE, FALSE, pszName) ) )
        {
            RemoveFromSOTable(LA_Table[i], MasterOwners);
            if( pszName )
            {
                WinsFreeMemory(pszName);
                pszName = NULL;
            }
            if( ResultsN.pAddVersMaps )
            {
                WinsFreeMem(ResultsN.pAddVersMaps);
                ResultsN.pAddVersMaps = NULL;
            }
            DisplayErrorMessage(EMSG_WINS_GETSTATUS_FAILED,
                                Status);
            continue;
        }
        else
        {
            DisplayMessage(g_hModule,
                           MSG_WINS_GETSTATUS_SUCCESS);
        }
        
        if (MasterOwners < ResultsN.NoOfOwners) 
        {

            ret = InitLATable(ResultsN.pAddVersMaps, MasterOwners, ResultsN.NoOfOwners);
            if( LA_Table is NULL or
                SO_Table is NULL )
            {
                DisplayMessage(g_hModule,
                               EMSG_WINS_OUT_OF_MEMORY);
                if( pszName )
                {
                    WinsFreeMemory(pszName);
                    pszName = NULL;
                }
                if( ResultsN.pAddVersMaps )
                {
                    WinsFreeMem(ResultsN.pAddVersMaps);
                    ResultsN.pAddVersMaps = NULL;
                }
                
                if( SO_Table )
                {
                    WinsFreeMemory(SO_Table);
                    SO_Table = NULL;
                }
                if( LA_Table )
                {
                    WinsFreeMemory(LA_Table);
                    LA_Table = NULL;
                }

                if( wszStartIp )
                {
                    WinsFreeMemory(wszStartIp);
                    wszStartIp = NULL;
                }

                return;
            }

            MasterOwners = ret;
        }

        //
        // Place entry in the SO Table in proper order
        //
        AddSOTableEntry(LA_Table[i], ResultsN.pAddVersMaps, ResultsN.NoOfOwners, MasterOwners);
        if( pszName )
        {
            WinsFreeMemory(pszName);
            pszName = NULL;
        }

        if( ResultsN.pAddVersMaps )
        {
            WinsFreeMem(ResultsN.pAddVersMaps);
            ResultsN.pAddVersMaps = NULL;
        }

    }

    //
    // Check if diagonal elements in the [SO] table are the highest in their cols.
    //
    CheckSOTableConsistency(MasterOwners);
    
    DumpSOTable(MasterOwners,
                fFile,
                pFile);

    //
    // Destroy SO table
    //
    FreeSOTable(MasterOwners+1);
    FreeLATable(MasterOwners);
    
}