//
//  $Log: Envoy.c,v $
//  Revision 1.5  1994/09/10  14:31:36  hakan
//  Sch...  Wenn die MATCH_HOSTNAME sagen meinen die auch
//  HOSTNAME und nicht REALMNAME:HOSTNAME
//
//  Revision 1.4  1994/08/15  22:22:14  hakan
//  'Stop Scanning' actually works now :-)
//
//  Revision 1.3  1994/07/12  23:10:46  hakan
//  Should be finished
//
//  Revision 1.2  1994/06/19  15:42:39  hakan
//  Starting,  tells whether we can hope for a realm server or not
//
//  Revision 1.1  1994/06/19  15:24:02  hakan
//  Initial revision
//
//

static char* RCSId = "$Id: Envoy.c,v 1.5 1994/09/10 14:31:36 hakan Exp $";



#include "extern.h"



static long         ScanBlock;
static long         _RealmCount;
static struct List  _RealmList;
static struct List  _HostList;
static struct Hook  h;
static ULONG        NIPCRetVal;
static BOOL         KeepScanning;
static BOOL         Kludge402;

static void ClearRealmList (void);
static void ClearHostList (void);
static void SortAllLists (void);
static void SortList (struct List* list);

static void DoScanHosts (char* realm);
static void StartInquiryA (APTR Tags);
__saveds static ULONG ASM hookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *msg);
__saveds static ULONG DecodeNIPCInquiry (struct Hook *h, VOID *o, VOID *msg);

#ifdef SERIAL_DEBUGGING
    #define D(x)    x
    extern KPrintF (char* format, ...);
#else
    #define D(x)
#endif



//
//  Publicly accessable functions
//



BOOL CreateEnvoy (void)
{
    NewList (&_RealmList);
    NewList (&_HostList);

    h.h_Entry       = (ULONG (*)()) hookEntry;
    h.h_SubEntry    = DecodeNIPCInquiry;
    h.h_Data        = NULL;

    return (TRUE);
}



void DestroyEnvoy (void)
{
    ClearRealmList ();
    ClearHostList ();
}



BOOL RealmServerRunning (void)
{
    char            HostName [MAXNAME+1];
    struct Entity*  local;
    BOOL            running = FALSE;


    if (local = CreateEntity (TAG_END))
    {
        if (GetHostName (local, HostName, sizeof (HostName) - 1))
        {
            if (strchr (HostName, ':'))
            {
                running = TRUE;
            }
        }
        
        DeleteEntity (local);
    }


    return (running);
}



struct List* Hosts (void)
{
    return (&_HostList);
}



struct List* Realms (void)
{
    return (&_RealmList);
}



Host* getHost (char* name)
{
    return ((Host*) FindName (&_HostList, name));
}



//
//  Here come various flavors of 'ScanFoo'-functions...
//



void ScanRealms (void)
{
    struct TagItem queryTags[] =
    {
        QUERY_REALMS,   0,
        TAG_END,        0,
    };


    ClearRealmList ();
    if (OpenScanWindow (NP_ScanTime * 5))
    {
        ScanBlock = 0;
        StartInquiryA (&queryTags);
        CloseScanWindow ();
    }
    SortAllLists ();
}



void ScanHosts (char* realmname)
{
    ClearHostList ();
    if (OpenScanWindow (NP_ScanTime * 5))
    {
        ScanBlock = 0;
        DoScanHosts (realmname);
        CloseScanWindow ();
    }
    SortAllLists ();
}



void ScanAll (void)
{
    struct TagItem queryTags[] =
    {
        QUERY_REALMS,   0,
        TAG_END,        0,
    };


    ClearRealmList ();
    if (OpenScanWindow (NP_ScanTime * 5))
    {
        KeepScanning = TRUE;
        ScanBlock = 0;
        StartInquiryA (&queryTags);
        if (_RealmCount)
        {
            if (OpenScanWindow (_RealmCount * NP_ScanTime * 5))
            {
                struct Node* realm;

                for (realm = _RealmList.lh_Head; realm->ln_Succ; realm = realm->ln_Succ)
                {
                    if (KeepScanning)
                    {
                        DoScanHosts (realm->ln_Name);
                        ScanBlock += 5 * NP_ScanTime;
                    }
                }
            }
        }

        CloseScanWindow ();
    }
    SortAllLists ();
}



void ScanPing (void)
{
    ClearHostList ();
    if (OpenScanWindow (NP_ScanTime * 5))
    {
        ScanBlock = 0;
        DoScanHosts (NULL);
        CloseScanWindow ();
    }
    SortAllLists ();
}



static void DoScanHosts (char* realm)
{
    long n = 0;
    struct TagItem queryTags [100];



    //
    //  Do we need our filter-kludge?
    //

    if (NP_Config & NP_402_Kludge)
    {
        Kludge402 = TRUE;
    }
    else
    {
        Kludge402 = FALSE;
    }


    //
    //  Build the Tag array
    //

    //
    //  We ALWAYS want to hear our Hostname
    //

    queryTags [n].ti_Tag    = (ULONG) QUERY_HOSTNAME;
    queryTags [n].ti_Data   = (ULONG) 0;
    n++;


    //
    //  Special Barnard Request
    //

    if (NP_Config & NP_SingleHost)
    {
        char* banane = strchr (NP_ScanHost, ':');
        
        queryTags [n].ti_Tag    = (ULONG) MATCH_HOSTNAME;
        queryTags [n].ti_Data   = (ULONG) (banane ? (banane+1) : NP_ScanHost);
        n++;
    }


    //
    //  Maybe a realm,  too?
    //

    if (realm)
    {
        queryTags [n].ti_Tag  = (ULONG) MATCH_REALM;
        queryTags [n].ti_Data = (ULONG) realm;
        n++;
    }


    //
    //  Whom does this Host belong?
    //

    if (NP_Config & NP_Owner)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_OWNER;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What IP-Adress do we have?  (Might not be supported in later versions)
    //

    if (NP_Config & NP_IPAddr)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_IPADDR;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Tell us your Kickstart-version
    //

    if (NP_Config & NP_KickVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_KICKVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Tell your Workbench, too
    //

    if (NP_Config & NP_WBVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_WBVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Some might care about the NIPC-Library
    //

    if (NP_Config & NP_NIPCVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_NIPCVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What kind of Fast Memory do we have?
    //

    if (NP_Config & NP_MaxFastMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_MAXFASTMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }

    if (NP_Config & NP_AvailFastMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_AVAILFASTMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What kind of Chip Memory do we have?
    //

    if (NP_Config & NP_MaxChipMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_MAXCHIPMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }

    if (NP_Config & NP_AvailChipMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_AVAILCHIPMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  How about our Processor / FPU
    //

    if (NP_Config & NP_ATTNFlags)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_ATTNFLAGS;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What Custom chips do we have?
    //

    if (NP_Config & NP_ChipRevBits)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_CHIPREVBITS;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Show us all your Services
    //

    if (NP_Config & NP_Services)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_SERVICE;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Show all your Entities, too
    //

    if (NP_Config & NP_Entities)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_ENTITY;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Simply clean up
    //

    queryTags [n].ti_Tag        = (ULONG) TAG_END;
    queryTags [n].ti_Data       = (ULONG) 0;


    //
    //  Start the Inquiry
    //

    StartInquiryA ((APTR) &queryTags);
}



//
//  Everything from here on is private...
//  First my Exec-list-handling functions
//



static void ClearRealmList (void)
{
    struct Node* work;
    struct Node* next;

    if (!(IsListEmpty (&_RealmList)))
    {
        work = _RealmList.lh_Head;
        while (next = work->ln_Succ)
        {
            FreeMem (work, sizeof (Realm));
            work = next;
        }
    }

    NewList (&(_RealmList));
    _RealmCount = 0;
}



static void ClearHostList (void)
{
    struct Node* work;
    struct Node* next;

    struct Node* innerwork;
    struct Node* innernext;

    Host*       innerhost;


    if (!(IsListEmpty (&_HostList)))
    {
        work = _HostList.lh_Head;
        while (next = work->ln_Succ)
        {
            innerhost = (Host*) work;

            if (!(IsListEmpty (&(innerhost->Entities))))
            {
                innerwork = innerhost->Entities.lh_Head;
                while (innernext = innerwork->ln_Succ)
                {
                    FreeMem (innerwork, sizeof (String));
                    innerwork = innernext;
                }
            }

            if (!(IsListEmpty (&(innerhost->Services))))
            {
                innerwork = innerhost->Services.lh_Head;
                while (innernext = innerwork->ln_Succ)
                {
                    FreeMem (innerwork, sizeof (String));
                    innerwork = innernext;
                }
            }

            FreeMem (work, sizeof (Host));
            work = next;
        }
    }

    NewList (&(_HostList));
    //  HostCount = 0;
}



static void SortAllLists (void)
{
    if (!(IsListEmpty (&_RealmList)))
    {
        SortList (&_RealmList);
    }


    if (!(IsListEmpty (&_HostList)))
    {
        struct Node*    work;
        struct List*    list;

        SortList (&_HostList);

        for (work = (struct Node*) _HostList.lh_Head; work->ln_Succ; work = work->ln_Succ)
        {
            list = (struct List*) (&((Host*) work)->Services);

            if (!(IsListEmpty (list)))
            {
                SortList (list);
            }

            list = (struct List*) (&((Host*) work)->Entities);

            if (!(IsListEmpty (list)))
            {
                SortList (list);
            }
        }
    }
}



//
//  This one is courtesy of Henning Schmiedehausen
//



static void SortList (struct List* list)
{
    struct Node* mynode;
    struct Node* my2node;
    BOOL         sorted;

    do
    {
        sorted = TRUE;
        for (mynode = (struct Node*) list->lh_Head; mynode->ln_Succ; mynode = mynode->ln_Succ)
        {
            if (mynode->ln_Succ->ln_Succ)
            {
                if (strcmp (mynode->ln_Name, mynode->ln_Succ->ln_Name) > 0)
                {
                    my2node = mynode->ln_Succ;

                    Remove (my2node);

                    my2node->ln_Pred         = mynode->ln_Pred;
                    my2node->ln_Succ         = mynode;

                    mynode->ln_Pred->ln_Succ = my2node;
                    mynode->ln_Pred          = my2node;

                    sorted = FALSE;
                }
            }
        }
    }
    while (!sorted);
}



//
//  Here goes real nipc stuff
//



static void StartInquiryA (APTR Tags)
{
    NIPCRetVal = TRUE;

    if (NIPCInquiryA (&h, NP_ScanTime, NP_ScanPackets, Tags))
    {
        long i;

        for (i=0; i<NP_ScanTime*5; i++)
        {
            if (ShowScan (ScanBlock + i))
            {
                i = NP_ScanTime * 5;
                KeepScanning = FALSE;
            }
        }
    }
    else
    {
        DisplayBeep (NULL);
    }

    //
    //  Tell nipc to stop sending packets...
    //

    NIPCRetVal = FALSE;
}



__saveds static ULONG ASM hookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *msg)
{
    return ((*(ULONG (*)(struct Hook *,VOID *,VOID *))(h->h_SubEntry))(h, o, msg));
}



__saveds static ULONG DecodeNIPCInquiry (struct Hook *h, VOID *o, VOID *msg)
{
    String*         string;
    Realm*          realm;
    Host*           host;

    struct TagItem* tstate;
    struct TagItem* tag;

    D (KPrintF ("DecodeNIPCInquiry() entered\n"));


    //
    //  The 'Object' is the address of the NetProbe Task.
    //  I've got no use for this...
    //

    //  if (o)
    //  {
    //  }


    //
    //  Here goes the 40.2-Kludge
    //

    if (Kludge402)
    {
        tstate = (struct TagItem*) msg;

        while (tag = NextTagItem (&tstate))
        {
            if (tag->ti_Tag ==  QUERY_NIPCVERSION)
            {
                if (tag->ti_Data < ((40 << 16) | 2))
                {
                    return TRUE;
                }
            }
        }
    }


    //
    //  Now, we need out working Host
    //

    tstate = (struct TagItem*) msg;

    while (tag = NextTagItem (&tstate))
    {
        if (tag->ti_Tag == QUERY_HOSTNAME)
        {
            if (!(host = (Host*) FindName (&(_HostList), (char*) tag->ti_Data)))
            {
                if (!(host = (Host*) AllocMem (sizeof (Host), MEMF_PUBLIC | MEMF_CLEAR)))
                {
                    return FALSE;
                }

                strcpy (host->HostName, (char*) tag->ti_Data);
                host->h_node.ln_Name = host->HostName;

                AddHead (&(_HostList), host);

                //  HostCount++;

                NewList (&(host->Entities));
                NewList (&(host->Services));
            }
        }
    }


    //
    //  Now, we fill in our real data
    //

    tstate = (struct TagItem*) msg;

    while (tag = NextTagItem (&tstate))
    {
        ULONG tidata = tag->ti_Data;

        switch (tag->ti_Tag)
        {
            case QUERY_IPADDR:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_IPADDR: '%ld.%ld.%ld.%ld'\n", ((tidata >> 24) & 0xff), ((tidata >> 16) & 0xff), ((tidata >> 8) & 0xff), (tidata & 0xff)));
                host->IPAddr = tidata;
                break;

            case QUERY_REALMS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_REALMS: '%s'\n", (char*) tidata));
                if (!(realm = (Realm*) FindName (&(_RealmList), (char*) tidata)))
                {
                    if (!(realm = AllocMem (sizeof (Realm), MEMF_PUBLIC|MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (realm->RealmName, (char*) tidata);
                    realm->r_node.ln_Name = realm->RealmName;

                    D (KPrintF ("DecodeNIPCInquiry:: '%s' added\n", realm->RealmName));
                    AddHead (&(_RealmList), realm);

                    _RealmCount++;
                }
                break;

            case QUERY_HOSTNAME:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_HOSTNAME: '%s'\n", (char*) tidata));
                break;

            case QUERY_SERVICE:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_SERVICE: '%s'\n", (char*) tidata));
                if (!(string = (String*) FindName (&(host->Services), (char*) tidata)))
                {
                    if (!(string = AllocMem (sizeof (String), MEMF_PUBLIC | MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (string->String, (char*) tidata);
                    string->t_node.ln_Name = string->String;
                    AddTail (&(host->Services), string);
                }
                break;

            case QUERY_ENTITY:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_ENTITY: '%s'\n", (char*) tidata));
                if (!(string = (String*) FindName (&(host->Entities), (char*) tidata)))
                {
                    if (!(string = AllocMem (sizeof (String), MEMF_PUBLIC | MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (string->String, (char*) tidata);
                    string->t_node.ln_Name = string->String;
                    AddTail (&(host->Entities), string);
                }
                break;

            case QUERY_OWNER:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_OWNER: '%s'\n", (char*) tidata));
                strcpy (host->Owner, (char*) tidata);
                break;

            case QUERY_MACHDESC:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MACHDESC: '%08lx'\n", tidata));
                break;

            case QUERY_ATTNFLAGS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_ATTNFLAGS: '%08lx'\n", tidata));
                host->ATTNFlags = tidata;
                break;

            case QUERY_LIBVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_LIBVERSION: '%08lx'\n", tidata));
                break;

            case QUERY_CHIPREVBITS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_CHIPREVBITS: '%08lx'\n", tidata));
                host->ChipRevBits = tidata;
                break;

            case QUERY_MAXFASTMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MAXFASTMEM: '%8ld'\n", tidata));
                host->MaxFastMem = tidata;
                break;

            case QUERY_AVAILFASTMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_AVAILFASTMEM: '%8ld'\n", tidata));
                host->AvailChipMem = tidata;
                break;

            case QUERY_MAXCHIPMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MAXCHIPMEM: '%8ld'\n", tidata));
                host->MaxChipMem = tidata;
                break;

            case QUERY_AVAILCHIPMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_AVAILCHIPMEM: '%8ld'\n", tidata));
                host->AvailChipMem = tidata;
                break;

            case QUERY_KICKVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_KICKVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->KickVersion = tidata;
                break;

            case QUERY_WBVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_WBVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->WBVersion = tidata;
                break;

            case QUERY_NIPCVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_NIPCVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->NIPCVersion = tidata;
                break;

            default:
                break;
        }
    }
    

    D (KPrintF ("DecodeNIPCInquiry() left\n\n"));

    return NIPCRetVal;
}
