/*

   HBBS (C) 1995 Hydra/LSD - All Rights Reserved!

   If you are reading this source and make *ANY* change or fix or whatever
   then please send a copy to me at the following address...

   D.Clifton, 9 Shires Copse, Southbourne, Bournemouth, Dorset, BH6 4AL, ENGLAND!

   All Suggestions are most welcome as if *you* think something needs changing
   then it probably does,  this program is supposed to be designed for the
   user, not the programmer of it!!

   Thanks..

   Note: if you want a better KS3.0 only version then #define KS30 otherwise #define KS20..
   and if you want to run cpr on the node program then #define DEBUGNODE
*/


/*

  create linked list of TimeAccessData at startup

*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/locale.h>
#include <libraries/reqtools.h>
#include <libraries/gadtools.h>
#include <diskfont/diskfont.h>
#include <utility/utility.h>
#include <graphics/gfxbase.h>
#include <graphics/scale.h>
#include <workbench/workbench.h>
#include <clib/locale_protos.h>
#include <clib/exec_protos.h>
#include <clib/wb_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/reqtools_protos.h>

#include <wb2cli.h>  /* for the correct PATH settings when run from WB */


UBYTE *versionstr="$VER: HBBS 0.8 (1.1.1996)";

#ifdef __SASC
int CXBRK(void) { return(0); }
int _CXBRK(void) { return(0); }
void chkabort(void) {}
#endif

#include "ControlGUI.h" // gui prototypes and external variables.

/* Main Stuff! */

#define MAIN

#include "/common/types.h"
#include "/common/defines.h"
#include "/common/errors.h"
#include "/common/files.h"
#include "/common/options.h"
#include "/common/structures.h"
#include "/common/strings.h"

#include "/library/hbbscommon_protos.h"
#include "/library/hbbscommon_pragmas.h"

struct ReqToolsBase *ReqToolsBase;
extern struct DosLibrary *DOSBase;
struct Window *OldWindowPtr;
struct Screen *Scr=NULL;
struct Library *HBBSCommonBase=NULL;

char StatusText[80];

UWORD CtrlScrnCoolPens[]=
{
  0+8,1+8,1+8,2+8,1+8,3+8,2+8,0+8,2+8,1+8,2+8,1+8,65535
};

/*
  DETAILPEN,0+8,
  BLOCKPEN,1+8,
  TEXTPEN,1+8,
  SHINEPEN,2+8,
  SHADOWPEN,1+8,
  FILLPEN,3+8,
  FILLTEXTPEN,2+8,
  BACKGROUNDPEN,0+8,
  HIGHLIGHTTEXTPEN,2+8,
  BARDETAILPEN,1+8,
  BARBLOCKPEN,2+8,
  BARTRIMPEN,1+8,

#define DETAILPEN        (0x0000)
#define BLOCKPEN         (0x0001)
#define TEXTPEN          (0x0002)
#define SHINEPEN         (0x0003)
#define SHADOWPEN        (0x0004)
#define FILLPEN          (0x0005)
#define FILLTEXTPEN      (0x0006)
#define BACKGROUNDPEN    (0x0007)
#define HIGHLIGHTTEXTPEN (0x0008)
#define BARDETAILPEN     (0x0009)
#define BARBLOCKPEN      (0x000A)
#define BARTRIMPEN       (0x000B)



*/

UWORD ColourArray[]=
{
  0x0000,
  0x0F00,
  0x00F0,
  0x0FF0,
  0x000F,
  0x0F0F,
  0x00FF,
  0x0FFF,
  0x0999,
  0x0000,
  0x0FFF,
  0x057A,
};

// global vars

UWORD offx;
UWORD offy;
WORD last_x=0;
WORD last_y=0;
LONG last_sec=0;
LONG last_mic=0;

BOOL CfgOpen=FALSE;

struct Process *CtrlProc=NULL;
struct BBSGlobalData *BBSGlobal=NULL;
struct TextFont *HBBSFont;


#ifdef KS20
UWORD LastNodeClicked=0;  //due to GT_GetGadgetAttrs() being V39+ only we have to
                          // make a note of the last node number clicked on in
                          // the Nodes Listview! Argh!
UWORD LastCycleClicked=0; // (and in the List Cycle Gadget Clicked..)
#endif

//defines for struct BBSGlobalData

UBYTE *FILE_SCRMODEPREFS           = "HBBS:System/Data/CtrlScrPrefs.CFG";

UBYTE *OPT_BBS_BBSName             = "BBSName",
      *OPT_BBS_BBSSerial           = "BBSSerial",
      *OPT_BBS_BBSLocation         = "BBSLocation",
      *OPT_BBS_BBSCountry          = "BBSCountry",
      *OPT_BBS_BBSGroup            = "BBSGroup",
      *OPT_BBS_BBSNodes            = "BBSNodes",
      *OPT_BBS_SysopAccount        = "SysopAccount",
      *OPT_BBS_Drive               = "Drive",
      *OPT_BBS_BBSDrive            = "BBSDrive",
      *OPT_BBS_MinFreeSpace        = "MinFreeSpace",
      *OPT_BBS_NoFreeSpaceScript   = "NoFreeSpaceScript",
      *OPT_BBS_ButtonName          = "ButtonName",
      *OPT_BBS_ButtonCMD           = "ButtonCMD",
      *OPT_BBS_HideScreen          = "HideScreen",
      *OPT_BBS_ErrorLogFile        = "ErrorLogFile",
      *OPT_BBS_ScrModeID           = "ScrModeID",
      *OPT_BBS_ScrWidth            = "ScrWidth",
      *OPT_BBS_ScrHeight           = "ScrHeight",
      *OPT_BBS_ScrDepth            = "ScrDepth",
      *OPT_BBS_UserDataFileName    = "UserDataFileName",
      *OPT_BBS_NewUserAccessLevel  = "NewUserAccessLevel",
      *OPT_BBS_MaxUsernameAttempts = "MaxUsernameAttempts",
      *OPT_BBS_MaxPasswordAttempts = "MaxPasswordAttempts",
      *OPT_BBS_MinPasswordLength   = "MinPasswordLength",
      *OPT_BBS_LanguageName        = "LanguageName",
      *OPT_BBS_LanguageExtn        = "LanguageExtn",
      *OPT_BBS_EditorCMD           = "EditorCMD",
      *OPT_BBS_CopyBufferSize      = "CopyBufferSize";

UBYTE *OPT_CONFL_Conferences       = "Conferences",
      *OPT_CONFL_ConfPath          = "ConfPath",
      *OPT_CONFL_NewUserConf       = "NewUserConf";

UBYTE *OPT_CONF_ConfName           = "ConfName",
      *OPT_CONF_ConfActive         = "ConfActive",
      *OPT_CONF_ConfAccess         = "ConfAccess",
      *OPT_CONF_HoldFileList       = "HoldFileList",
      *OPT_CONF_HoldFiles          = "HoldFiles",
      *OPT_CONF_BadFileList        = "BadFileList",
      *OPT_CONF_BadFiles           = "BadFiles",
      *OPT_CONF_PartUpload         = "PartUpload",
      *OPT_CONF_FileList           = "FileList",
      *OPT_CONF_Upload             = "Upload",
      *OPT_CONF_Download           = "Download",
      *OPT_CONF_MaxDIZLines        = "MaxDIZLines",
      *OPT_CONF_MenuPrompt         = "MenuPrompt",
      *OPT_CONF_AutoMailScan       = "AutoMailScan",
      *OPT_CONF_ConfPassWD         = "ConfPassWD",
      *OPT_CONF_UserAllowed        = "UserAllowed";

UBYTE *OPT_LLIST_LEVEL             = "Level",
      *OPT_LLIST_LEVELNAME         = "Level_Name";

#include "/common/Shared.c"

extern UWORD    CtrlScrndefpens;

extern UWORD   CtrlScrnColors[];

void About( void )
{
  char buffer[1024];

  sprintf(buffer,"HydraBBS %s (C) 1995 Deluxe Software Ltd.\n\n"
#ifdef BETA
                 "BETA VERSION, DO NOT SPREAD!\n\n"
#endif
                 "HBBS is a cool BBS System programed with users and sysops in mind\n"
                 "unlike the ageing LAmiExpress which it is designed to replace.\n\n"
                 "This software is SHAREWARE, if you use it then send a registration\n"
                 "fee of £20 (UK Pounds Sterling) made Payable to D.Clifton\n\n"
                 "To Deluxe Software Ltd, 9 Shires Copse, Bournemouth, Dorset, BH6 4AL, ENGLAND\n\n"
                 "And you'll receive telephone support and the FULL Source Code, *and*\n"
                 "you'll get all the updates and new doors programmed by as and when they\n"
                 "are released.  See the docs for more info!\n\n"
                 "Note: NOTHING IS DISABLED OR CUT-DOWN THIS ISTHE FULL THING!\n",versionstr);
  HBBS_rterror(buffer);
}

void PrintList( struct List *list)
{
  struct Node *node;
  for (node = list->lh_Head ; node->ln_Succ ; node =node->ln_Succ)
  {
    puts(node->ln_Name);
  }
}



BOOL AllNodesShut( void )
{
  short NodesShut=0;
  struct NodeData *nd;
  for (nd = (struct NodeData*)BBSGlobal->NodeList->lh_Head ; nd->node.ln_Succ ; nd = (struct NodeData*)nd->node.ln_Succ)
  {
    if (nd->Status==STAT_CLOSED) NodesShut++;
  }
  if (NodesShut==BBSGlobal->BBSNodes) return(TRUE);
  return(FALSE);
}

struct Screen *OpenCtrlScrn(void)
{
  struct Screen *NewScr;

  if (NewScr=OpenScreenTags(NULL,    (SA_Title),(ULONG)"HBBS Main Control Screen (C) 1995 Hydra of LSD!",
    (SA_PubName),(ULONG)str_CtrlScrnName,
    (SA_Left)    ,0,
    (SA_Top)     ,0,
    (SA_Width)   ,BBSGlobal->ScrWidth,
    (SA_Height)  ,BBSGlobal->ScrHeight,
    (SA_DisplayID),BBSGlobal->ScrModeID,
    (SA_Depth)   ,BBSGlobal->ScrDepth,
    (SA_Overscan),1,
    (SA_Font),(ULONG)&HBBS8066,
    (SA_Behind),1,
    (SA_Quiet),0,
    (SA_ShowTitle),1,
    (SA_AutoScroll),1,
    (SA_FullPalette),1,
//    (SA_ErrorCode),(ULONG)(&CtrlScrnError),
    (SA_Pens), BBSGlobal->ScrDepth<4 ? (ULONG)&CtrlScrndefpens : (ULONG)&CtrlScrnCoolPens,
    (SA_Colors),(ULONG)CtrlScrnColors,
    (TAG_DONE)))
  {
    if (BBSGlobal->ScrDepth>=4)
    {
      LoadRGB4(&NewScr->ViewPort,ColourArray,12);
      SetAPen(&NewScr->RastPort,8);
      Flood(&NewScr->RastPort,1,0,NewScr->WBorTop+4);
    }
  }
  return(NewScr);
}

BOOL PickScreen( void )
{
  BOOL retval=FALSE;
  struct rtScreenModeRequester *scrmodereq;

  if (scrmodereq = rtAllocRequestA (RT_SCREENMODEREQ, NULL))
  {
    if (rtScreenModeRequest (scrmodereq, "Select Screen mode:",RTSC_Flags,SCREQF_GUIMODES | SCREQF_DEPTHGAD | SCREQF_SIZEGADS,
                                                               RT_Window,Ctrl,
                                                               RTSC_MinHeight,200,
                                                               RTSC_MinWidth,640,
                                                               RTSC_MinDepth,2,
                                                               RTSC_MaxDepth,4,

                                                               TAG_END))
    {
      BBSGlobal->ScrModeID=scrmodereq->DisplayID;
      BBSGlobal->ScrHeight=scrmodereq->DisplayHeight;
      BBSGlobal->ScrWidth=scrmodereq->DisplayWidth;
      BBSGlobal->ScrDepth=scrmodereq->DisplayDepth;
      retval=TRUE;
    }
    rtFreeRequest(scrmodereq);
  }
  return(retval);
}

BOOL LoadScreenPrefs( void )
{
  struct CfgFileData *ScrCfg;
  BOOL OK=FALSE;

  if (ScrCfg=HBBS_LoadConfig(FILE_SCRMODEPREFS,LCFG_NONE))
  {
    if (HBBS_GetSetting(ScrCfg,(void *)&BBSGlobal->ScrModeID,VTYPE_BIGNUM,OPT_BBS_ScrModeID,OPT_SINGLE))
    if (HBBS_GetSetting(ScrCfg,(void *)&BBSGlobal->ScrHeight,VTYPE_BIGNUM,OPT_BBS_ScrHeight,OPT_SINGLE))
    if (HBBS_GetSetting(ScrCfg,(void *)&BBSGlobal->ScrWidth,VTYPE_BIGNUM,OPT_BBS_ScrWidth,OPT_SINGLE))
    if (HBBS_GetSetting(ScrCfg,(void *)&BBSGlobal->ScrDepth,VTYPE_BIGNUM,OPT_BBS_ScrDepth,OPT_SINGLE)) OK=TRUE;

    HBBS_FlushConfig(ScrCfg);
  }
  else
  {
    HBBS_rterror("Use the \"Select_Mode\" Utility to setup a screenmode!");
  }
  return(OK);
}

void SaveScreenPrefs( void )
{
  struct CfgFileData *ScrCfg;
  char params[30];
  if (ScrCfg=HBBS_CreateConfig((FILE_SCRMODEPREFS)))
  {
    sprintf(params,"%d",BBSGlobal->ScrModeID);
    HBBS_AddCfgItem(ScrCfg,OPT_BBS_ScrModeID,params);
    sprintf(params,"%d",BBSGlobal->ScrHeight);
    HBBS_AddCfgItem(ScrCfg,OPT_BBS_ScrHeight,params);
    sprintf(params,"%d",BBSGlobal->ScrWidth);
    HBBS_AddCfgItem(ScrCfg,OPT_BBS_ScrWidth,params);
    sprintf(params,"%d",BBSGlobal->ScrDepth);
    HBBS_AddCfgItem(ScrCfg,OPT_BBS_ScrDepth,params);

    if (!HBBS_SaveConfig(ScrCfg))
    {
      HBBS_rterror("Error Saving Screenmode Prefs!");
    }
    HBBS_FlushConfig(ScrCfg);
  }
}

void UpdateInfoLists( void )
{
  struct NodeData *nd;

  // *?* should we set gadgetatrs() the cycle gadget just to make sure.. ?


  // set the list to be BLANK
  GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,NULL,TAG_DONE);

  if (nd=HBBS_NodeDataPtr(LastNodeClicked)) // code contains number of listview selected
  {
    if ((nd->Status==STAT_ONLINE) || (nd->Status==STAT_READY))
    {
      switch (LastCycleClicked)
      {
        case 0: // Last Callers
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_Callers,TAG_DONE);
          break;
        case 1: // Last Downloads
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_Downloads,TAG_DONE);
          break;
        case 2: // Last Uploads
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_Uploads,TAG_DONE);
          break;
        case 3: // Last Pagers
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_Pagers,TAG_DONE);
          break;
        case 4: // Last Fails
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_PWFails,TAG_DONE);
          break;
        case 5: // Last Carrier
          GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,nd->Last_Carrier,TAG_DONE);
          break;
      }
    }
  }
}

void updategadgets( void )
{
  GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Labels,BBSGlobal->NodeList,GTLV_Top,0,TAG_DONE);
  GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV3_Commands],Ctrl,NULL,GTLV_Labels,BBSGlobal->ButtonName,GTLV_Top,0,TAG_DONE);
  UpdateInfoLists();
}

// small routing to check that all the windows on a screen have been closed..

void CloseAllWindows(struct Screen *Scr)
{
  if ((Scr) && (Scr->FirstWindow))
  {
    while (Scr->FirstWindow->NextWindow)
    {
      rtEZRequest("Please Close All Windows Before Clicking OK","OK",NULL,NULL,NULL);
    }
  }
}

void CloseTheScreen( void )
{
  if (CfgOpen)
  {
    CloseCtrlCfgWindow();
    CfgOpen=FALSE;
  }
  CloseAllWindows(Scr);

  if (Ctrl)
  {
    CloseCtrlWindow();
    CtrlProc->pr_WindowPtr=OldWindowPtr;
  }
  if (Scr) CloseScreen(Scr);
}

BOOL OpenTheScreen( void )
{
  BOOL Cancel=FALSE;

  Ctrl=NULL;
  CfgOpen=FALSE;

  do
  {
    if (Scr = OpenCtrlScrn())
    {
      BBSGlobal->scr=Scr;
      PubScreenStatus(Scr,0);
      if (OpenCtrlWindow(Scr)==0)
      {
        // bring screen to front..
        if (!BBSGlobal->HideScreen) ScreenToFront(Scr);
        OldWindowPtr=CtrlProc->pr_WindowPtr;
        CtrlProc->pr_WindowPtr=Ctrl;
        offx = Ctrl->BorderLeft;
        offy = Ctrl->BorderTop;
        updategadgets();
        return(TRUE);
      }
      else HBBS_DoErrorMessage(EMSG_NOWINDOW,0,NULL);
    }
    else HBBS_DoErrorMessage(EMSG_NOSCREEN,0,NULL);
    if (Scr==NULL)
    {
      Cancel=!PickScreen();
    }
  } while (Cancel==FALSE);

  CloseTheScreen();
  return(FALSE);
}

void ChangeScreenMode( void )
{
  if (AllNodesShut())
  {
    PickScreen();
    CloseTheScreen();
    OpenTheScreen();
  }
  else HBBS_DoErrorMessage(EMSG_CANTCHANGESCREEN,0,NULL);
}


void UpdateCtrlStatus(V_BIGNUM Flags,V_BIGNUM Value)
{
  switch (Flags)
  {
    case UPD_NODESTATUS:
      switch (Value)
      {
        case STAT_CLOSED:
          strcpy(StatusText,str_STAT_CLOSED);
          break;
        case STAT_LOADING:
          strcpy(StatusText,str_STAT_LOADING);
          break;
        case STAT_INITIALIZING:
          strcpy(StatusText,str_STAT_INITIALIZING);
          break;
        case STAT_READY:
          strcpy(StatusText,str_STAT_READY);
          break;
        case STAT_ONLINE:
          strcpy(StatusText,str_STAT_ONLINE);
          break;
        case STAT_CLOSING:
          strcpy(StatusText,str_STAT_CLOSING);
          break;
        default:
          StatusText[0]=0;
          break;
      }
      break;
  }

  GT_SetGadgetAttrs(CtrlGadgets[Ctrl_StatusText],Ctrl,NULL,GTTX_Text,StatusText,TAG_DONE);


/*
  SetAPen(Ctrl->RPort,0); // grey
  RectFill( Ctrl->RPort, 64+1+offx,79+offy,offx+628,79+offy+9);
  SetAPen(Ctrl->RPort,1); // black!
  SetBPen(Ctrl->RPort,0); // grey!
  Move( Ctrl->RPort,64+1+offx,8+78+offy);
  Text( Ctrl->RPort,str,strlen(str));
*/
}


void ClearBBSGlobalData(struct BBSGlobalData *BG )
{
  BG->BBSName=NULL;
  BG->BBSSerial=0;
  BG->BBSLocation=NULL;
  BG->BBSCountry=NULL;
  BG->BBSGroup=NULL;
  BG->BBSNodes=0;
  BG->SysopAccount=NULL;
  BG->Drive=NULL;
  BG->BBSDrive=NULL;
  BG->MinFreeSpace=NULL;
  BG->NoFreeSpaceScript=NULL;
  BG->ButtonName=NULL;
  BG->ButtonCMD=NULL;
  BG->HideScreen=FALSE;
  BG->ErrorLogFile=NULL;
  BG->ScrModeID=0;
  BG->ScrHeight=0;
  BG->ScrWidth=0;
  BG->ScrDepth=0;
  BG->UserDataFileName=NULL;
  BG->NewUserAccessLevel=0;
  BG->MaxUsernameAttempts=0;
  BG->MaxPasswordAttempts=0;
  BG->MinPasswordLength=0;
  BG->Languages=0;
  BG->EditorCMD=NULL;
  BG->LastCalledDate[0]=0;
  BG->LastCalledTime[0]=0;
  BG->CallsToday=0;
}

void SetBBSGlobalDefaults(struct BBSGlobalData *BG )
{
  BG->BBSSerial=0;
  FreeAndSet(&BG->BBSLocation,"UnKnown");
  FreeAndSet(&BG->BBSCountry,"UnKnown");
  FreeAndSet(&BG->SysopAccount,"Sysop");
  BG->MinFreeSpace=4096;
  FreeAndSet(&BG->ErrorLogFile,FILE_ERRORLOG);
  BG->ScrModeID=167936; // pal hi-res screen..
  BG->ScrHeight=256;
  BG->ScrWidth=640;
  BG->ScrDepth=2;
  BG->CopyBufferSize=32768; // 32K

  FreeAndSet(&BG->UserDataFileName,"HBBS:System/Data/User.Data");
  BG->NewUserAccessLevel=0;
  BG->MaxUsernameAttempts=3;
  BG->MaxPasswordAttempts=3;
  BG->MinPasswordLength=4;
}

void FreeBBSGlobalData(struct BBSGlobalData *BG)
{
  FreeStr(BG->BBSName);
  FreeStr(BG->BBSLocation);
  FreeStr(BG->BBSCountry);
  FreeStr(BG->SysopAccount);
  FreeStr(BG->BBSDrive);
  FreeStr(BG->NoFreeSpaceScript);
  FreeStr(BG->ErrorLogFile);
  FreeStr(BG->UserDataFileName);
  FreeStr(BG->EditorCMD);
  FreeStrList(BG->BBSGroup);
  FreeStrList(BG->Drive);
  FreeStrList(BG->ButtonName);
  FreeStrList(BG->ButtonCMD);
  FreeStrList(BG->LanguageName);
  FreeStrList(BG->LanguageExtn);
}

V_ERROR LoadBBSGlobalData(char *filename,struct BBSGlobalData *BG )
{
  struct CfgFileData *BBSCFG;
  V_ERROR error=TYPE_NONE;

  ClearBBSGlobalData(BG);
  SetBBSGlobalDefaults(BG);
  if (BBSCFG=HBBS_LoadConfig(filename,LCFG_NONE))
  {
    // get settings!
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSNodes,VTYPE_BIGNUM,OPT_BBS_BBSNodes,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSName,VTYPE_STRING,OPT_BBS_BBSName,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSSerial,VTYPE_BIGNUM,OPT_BBS_BBSSerial,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSLocation,VTYPE_STRING,OPT_BBS_BBSLocation,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSCountry,VTYPE_STRING,OPT_BBS_BBSCountry,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSGroup,VTYPE_STRINGLIST,OPT_BBS_BBSGroup,OPT_MULTI);
    HBBS_GetSetting(BBSCFG,(void *)&BG->SysopAccount,VTYPE_STRING,OPT_BBS_SysopAccount,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->Drive,VTYPE_STRINGLIST,OPT_BBS_Drive,OPT_MULTI);
    HBBS_GetSetting(BBSCFG,(void *)&BG->BBSDrive,VTYPE_STRING,OPT_BBS_BBSDrive,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->MinFreeSpace,VTYPE_BIGNUM,OPT_BBS_MinFreeSpace,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->NoFreeSpaceScript,VTYPE_STRING,OPT_BBS_NoFreeSpaceScript,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->ButtonName,VTYPE_STRINGLIST,OPT_BBS_ButtonName,OPT_MULTI);
    HBBS_GetSetting(BBSCFG,(void *)&BG->ButtonCMD,VTYPE_STRINGLIST,OPT_BBS_ButtonCMD,OPT_MULTI);
    HBBS_GetSetting(BBSCFG,(void *)&BG->HideScreen,VTYPE_BOOL,OPT_BBS_HideScreen,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->ErrorLogFile,VTYPE_STRING,OPT_BBS_ErrorLogFile,OPT_SINGLE);
//    HBBS_GetSetting(BBSCFG,(void *)&BG->ScrModeID,VTYPE_BIGNUM,OPT_BBS_ScrModeID,OPT_SINGLE);
//    HBBS_GetSetting(BBSCFG,(void *)&BG->ScrHeight,VTYPE_BIGNUM,OPT_BBS_ScrHeight,OPT_SINGLE);
//    HBBS_GetSetting(BBSCFG,(void *)&BG->ScrWidth,VTYPE_BIGNUM,OPT_BBS_ScrWidth,OPT_SINGLE);
//    HBBS_GetSetting(BBSCFG,(void *)&BG->ScrDepth,VTYPE_BIGNUM,OPT_BBS_ScrDepth,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->UserDataFileName,VTYPE_STRING,OPT_BBS_UserDataFileName,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->NewUserAccessLevel,VTYPE_SMALLNUM,OPT_BBS_NewUserAccessLevel,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->MaxUsernameAttempts,VTYPE_SMALLNUM,OPT_BBS_MaxUsernameAttempts,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->MaxPasswordAttempts,VTYPE_SMALLNUM,OPT_BBS_MaxPasswordAttempts,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->MinPasswordLength,VTYPE_SMALLNUM,OPT_BBS_MinPasswordLength,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->LanguageName,VTYPE_STRINGLIST,OPT_BBS_LanguageName,OPT_MULTI);
    BG->Languages=HBBS_GetSetting(BBSCFG,(void *)&BG->LanguageExtn,VTYPE_STRINGLIST,OPT_BBS_LanguageExtn,OPT_MULTI);
    HBBS_GetSetting(BBSCFG,(void *)&BG->EditorCMD,VTYPE_STRING,OPT_BBS_EditorCMD,OPT_SINGLE);
    HBBS_GetSetting(BBSCFG,(void *)&BG->CopyBufferSize,VTYPE_BIGNUM,OPT_BBS_CopyBufferSize,OPT_SINGLE);


    HBBS_FlushConfig(BBSCFG);
  }
  return(error);
}

static VOID cleanup(ULONG num)
{
  CloseLoadingWindow();

  if (num)
  {
    if (HBBSCommonBase) HBBS_DoErrorMessage(num,0,NULL);
  }

  FreeImages();

  CloseFont( HBBSFont ) ;
  CloseLibs();

  if (HBBSCommonBase)
  {
    HBBS_CleanUpCommon();
    CloseLibrary (HBBSCommonBase);
  }

  if (BBSGlobal)
  {
    if (BBSGlobal->CtrlMainPort) DeletePort(BBSGlobal->CtrlMainPort);
    if (BBSGlobal->CtrlMainReplyPort) DeletePort(BBSGlobal->CtrlMainReplyPort);
    if (BBSGlobal->NodeGlobalData) FreeVec(BBSGlobal->NodeGlobalData);
    FreeVec(BBSGlobal);
  }

  if (ReqToolsBase)
    CloseLibrary ((struct Library *)ReqToolsBase);

  exit(0);
}

int OpenTheDiskFonts( void )
{
  int OKSoFar = 1;
if ((HBBSFont = OpenDiskFont( &HBBS8066 )) == NULL )
  OKSoFar = 0;
return ( OKSoFar );
}


void init(VOID) // will never return anything as if something in here fails the
{               // program is terminated

  struct MsgPort *tmpport;

  if (OpenLibs())
  {
    cleanup(EMSG_NOLIBS);
  }
  else
  {
    if (!OpenTheDiskFonts())
    {
      cleanup(EMSG_NOFONT);
    }
    else
    {
      if (!MakeImages())
      {
        if (!(ReqToolsBase = (struct ReqToolsBase *) OpenLibrary (REQTOOLSNAME, REQTOOLSVERSION)))
        {
          cleanup(EMSG_NOREQTOOLS);
        }

        if(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0))
        {
          if (HBBS_InitCommon())
          {


            Forbid();
            tmpport=FindPort(CtrlMainPortName);
            Permit();

            if (tmpport)
              cleanup(EMSG_ALREADYSTARTED);

            if (BBSGlobal=(struct BBSGlobalData*) AllocVec(sizeof(struct BBSGlobalData),MEMF_PUBLIC|MEMF_CLEAR))
            {
              if (BBSGlobal->NodeGlobalData=(struct NodeData*) AllocVec(sizeof(struct NodeData),MEMF_PUBLIC|MEMF_CLEAR))
              {
                if (BBSGlobal->CtrlMainPort=CreatePort(CtrlMainPortName,0))
                {
                  if (BBSGlobal->CtrlMainReplyPort=CreatePort(0,0))
                  {
                    // *C* move this line to just before the nodes start..
                    // then all the data will be ready for all external doors to use..
                    HBBS_SetBBS(BBSGlobal);
                    return;
                  }
                }
              }
            }
          }
        }
      }
      cleanup(EMSG_NOMEM);
    }
  }
}

void RequestNode(struct NodeData *ND,V_BIGNUM status)
{
  struct StatusMsg *SMsg;

  // memory will be free'd by NODE program (if node is alive of course!)
  if (SMsg=(struct StatusMsg *)AllocVec(sizeof(struct StatusMsg),MEMF_PUBLIC))
  {
    SMsg->message.mn_Node.ln_Type = NT_MESSAGE;
    SMsg->message.mn_ReplyPort=NULL;  // must set to null!
    SMsg->message.mn_Length=sizeof(struct StatusMsg);
    SMsg->MsgType=mtype_STATUS;
    SMsg->Status=status;
    SendMessage((struct Message*)SMsg,ND->PortName);
  }
}

// Main Routine to close down a node.

void StopNode(short nodenum)
{
  struct NodeData *nd;

  if (nd=HBBS_NodeDataPtr(nodenum))
  {
    RequestNode(nd,STAT_REQUESTCLOSE);
  }
}

void StartNode(short nodenum)
{
  struct NodeData *nd;
  static ULONG sysargs[] =
  {
    SYS_Input,NULL,
    SYS_Output,NULL,
    SYS_Asynch,TRUE,
    SYS_UserShell,TRUE,
    NP_Priority,0L,
    TAG_DONE
  };
  BPTR outputfile;
  BOOL ok=FALSE;
  char command[100]; // *C* Change me!!! :-)

  if (nd=HBBS_NodeDataPtr(nodenum))
  {
    if (nd->Status==STAT_CLOSED) // check to see if node is already open first.
    {
      nd->Status=STAT_LOADING;
      UpdateCtrlStatus(UPD_NODESTATUS,nd->Status);

#ifndef DEBUGNODE
      if (outputfile = Open(str_CONSOLE,MODE_OLDFILE))
      {
        sysargs[1]=(ULONG)outputfile;
        sprintf(command,"HBBS:Doors/System/Node %d",nodenum);

        if (SystemTagList(command,(struct TagItem *)sysargs)==0)
        {
          ok=TRUE;
        }
      }
      if (!ok)
      {
        nd->Status=STAT_CLOSED;
        HBBS_LogError(FILE_ERRORLOG,ERR_GENERAL,"Error Starting Node!",TYPE_CRITICAL);
      }
#else
      rtEZRequest("Start Node Program NOW!","OK!",NULL,NULL,NULL);
#endif
    }
  }
}

// only called once, this just starts all the nodes that have autostart enabled!

void StartNodes( void )
{
  struct NodeData *nd;

  for (nd = (struct NodeData*)BBSGlobal->NodeList->lh_Head ; nd->node.ln_Succ ; nd = (struct NodeData*)nd->node.ln_Succ)
  {
    if (nd->AutoStart) StartNode(nd->NodeNum);
  }
}

void StopAllNodes( void )
{
  struct NodeData *nd;

  for (nd = (struct NodeData*)BBSGlobal->NodeList->lh_Head ; nd->node.ln_Succ ; nd = (struct NodeData*)nd->node.ln_Succ)
  {
    // if node is not busy then tell it to shutdown..
    if (nd->Status==STAT_READY) StopNode(nd->NodeNum);
  }
}

void HandleStatusMsg( struct StatusMsg *SMsg )
{
  struct NodeData *nd;

  if (nd=HBBS_NodeDataPtr(SMsg->NodeNum))
  {
    GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Selected,nd->NodeNum,TAG_DONE);
#ifdef KS20
    LastNodeClicked=SMsg->NodeNum;
#endif
    // get the nodestatus node and set the status variable
    // actually the NODE program could do this but the control program would not be
    // aware, so we send a msg.. :-)

    nd->Status=SMsg->Status;

    UpdateCtrlStatus(UPD_NODESTATUS,nd->Status);

    if (nd->Status==STAT_CLOSED)
    {
      StopNode(SMsg->NodeNum);
      GT_SetGadgetAttrs(CtrlGadgets[Ctrl_LV1],Ctrl,NULL,GTLV_Labels,NULL,TAG_DONE);
    }
  }
  // reply wanted ??
  if (SMsg->message.mn_ReplyPort)
  {
    // yup, reply it then..
    ReplyMsg((struct Message *)SMsg);
  }
  else
  {
    // nope, free the mem..
    FreeVec(SMsg);
  }
}

void HandleAskMsg(struct AskMsg *AMsg)
{
  switch (AMsg->Flags)
  {
    case ASK_BBSGLOBAL:
      AMsg->Pointer=(void *)BBSGlobal;
      break;
    default:
      puts("UnKnown AskMsg Received!");
      break;
  }
  ReplyMsg((struct Message *)AMsg);
}

void HandleRequestMsg(struct RequestMsg *RMsg)
{
  switch (RMsg->Flags)
  {
    case REQ_UPDATEINFO:
      UpdateInfoLists();
      break;
    default:
      puts("UnKnown RequestMsg Received!");
      break;
  }
  ReplyMsg((struct Message *)RMsg);
}

// the message handler!

void HandleMsg(void )
{
  struct NodeMsg *Msg;

  while (Msg=(struct NodeMsg *)GetMsg(BBSGlobal->CtrlMainPort))
  {
    switch (Msg->MsgType)
    {
      case mtype_ASK:
        HandleAskMsg((struct AskMsg *)Msg);
        break;
      case mtype_STATUS:
        HandleStatusMsg((struct StatusMsg *)Msg);
        break;
      case mtype_REQUEST:
        HandleRequestMsg((struct RequestMsg *)Msg);
        break;
      default:
        puts("UnKnown Message Type!");
        break;
    }
  }
}

BOOL CheckDoubleClick(WORD MouseX,WORD MouseY,LONG sec,LONG mic)
{
  if (DoubleClick(last_sec,last_mic,sec,mic) && (MouseX==last_x && MouseY==last_y))
  {
    // clear values so we can doubleclick and doubleclick again without it recognising 3 clicks as being 2 doubleclicks..
    last_x=0;
    last_y=0;
    last_sec=0;
    last_mic=0;
    return(TRUE);
  }
  last_x=MouseX;
  last_y=MouseY;
  last_sec=sec;
  last_mic=mic;
  return(FALSE);
}

/* Menu Processing for CtrlMenu */
/* Just pass the code field from an IDCMP_MENUPICK or IDCMP_MENUHELP message. */

short ProcessMenuIDCMPCtrlMenu( UWORD MenuNumber)
{
  UWORD MenuNum;
  UWORD ItemNumber;
  UWORD SubNumber;
  short Done=0;           /* Set Done to 1 to forget rest of next selects. */
  short Quit=0;
  struct MenuItem *item;

  while ((MenuNumber != MENUNULL) && (Done == 0))
  {
    item = ItemAddress( CtrlMenu, MenuNumber);
    MenuNum = MENUNUM(MenuNumber);
    ItemNumber = ITEMNUM(MenuNumber);
    SubNumber = SUBNUM(MenuNumber);
    switch ( MenuNum )
    {
      case NOMENU :
        /* No Menu Selected. */
        break;
      case Menu_1 :
        switch ( ItemNumber )
        {
          case NOITEM :
            /* No Item selcted. */
            break;
          case CtrlMenu_About :
            /* Item Text : About */
            About();
            break;
          case CtrlMenu_ScreenMode :
            /* Item Text : Screen Mode */
            Done=1;
            ChangeScreenMode();
            break;
          case bar :
            /* Item Text : bar */
            break;
          case CtrlMenu_Qui :
            /* Item Text : Quit! */
            Done=1;
            Quit=1;
            break;
        }
        break;
    }
    if (!Done) MenuNumber = item->NextSelect;
  }
  return(Quit);
}

short ProcessWindowCtrl( LONG Class, UWORD Code, APTR IAddress, WORD MouseX, WORD MouseY, LONG Seconds, LONG Micros)
{
  #ifdef KS30
    LONG num;
  #endif

  char tmpstr[1024],filename[1024];
  struct NodeData *tempnode;
  struct Gadget *gad;
  struct NodeData *nd;
  short done=0;
  switch ( Class )
  {
    case IDCMP_MENUPICK:
      done=ProcessMenuIDCMPCtrlMenu(Code);
      break;
    case IDCMP_GADGETUP :
      /* Gadget message, gadget = gad. */
      gad = (struct Gadget *)IAddress;
      switch ( gad->GadgetID )
      {
        case Ctrl_LV1 :
          /* ListView pressed, Text of gadget :  */
          break;
        case Ctrl_LV1_Cycle :
          /* Cycle changed   , Text of gadget :  */ // *C* won't work is KS30 is defined..
          LastCycleClicked=Code;
          UpdateInfoLists();

          break;
        case Ctrl_LV1_Info :
          /* Button pressed  , Text of gadget : Information */
          break;
        case Ctrl_LV2_Nodes :
          /* ListView pressed, Text of gadget :  */
          if (Code>=0)
          {
#ifdef KS20
            LastNodeClicked=Code; // cos 2.04 is lAmE
#endif
            if (nd=HBBS_NodeDataPtr(Code)) // code contains number of listview selected
            {
              UpdateCtrlStatus(UPD_NODESTATUS,nd->Status);
              UpdateInfoLists();

              // right, if it's been double clicked then open the node if it's
              // currenty closed, if it's open then open it's screen..

              if (CheckDoubleClick(MouseX,MouseY,Seconds,Micros))
              {
                switch(nd->Status)
                {
                  case STAT_CLOSED:
                    StartNode(nd->NodeNum);
                    break;
                  case STAT_READY:
                  case STAT_ONLINE:
                    RequestNode(nd,STAT_OPENSCREEN);
                    break;
                }
              }
            }
          }
          break;
        case Ctrl_Window :
          /* Button pressed  , Text of gadget : Callers */
#ifdef KS30
          if (1==GT_GetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Selected,&num,TAG_DONE))
          {
            if (num>=0)
            {
              if (nd=HBBS_NodeDataPtr(num))
              {
                if ( nd->Status==STAT_READY || nd->Status==STAT_ONLINE )
                {
                  if (nd->NodeSettings.Iconified) RequestNode(nd,STAT_OPENWINDOW); else RequestNode(nd,STAT_CLOSEWINDOW);
                }
              }
            }
          }
#else
          if (nd=HBBS_NodeDataPtr(LastNodeClicked))
          {
            if ( nd->Status==STAT_READY || nd->Status==STAT_ONLINE )
            {
              if (nd->NodeSettings.Iconified) RequestNode(nd,STAT_OPENWINDOW); else RequestNode(nd,STAT_CLOSEWINDOW);
            }
          }
#endif
          break;
        case Ctrl_Screen :
          /* Button pressed  , Text of gadget : Screen */
#ifdef KS30
          if (1==GT_GetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Selected,&num,TAG_DONE))
          {
            if (num>=0)
            {
              if (nd=HBBS_NodeDataPtr(num))
              {
                if ( nd->Status==STAT_READY || nd->Status==STAT_ONLINE )
                {
                  if (nd->ConOK==FALSE) RequestNode(nd,STAT_OPENSCREEN); else RequestNode(nd,STAT_CLOSESCREEN);
                }
              }
            }
          }
#else
          if (nd=HBBS_NodeDataPtr(LastNodeClicked))
          {
            if ( nd->Status==STAT_READY || nd->Status==STAT_ONLINE )
            {
              if (nd->ConOK==FALSE) RequestNode(nd,STAT_OPENSCREEN); else RequestNode(nd,STAT_CLOSESCREEN);
            }
          }
#endif
          break;
        case Ctrl_Stop :
          /* Button pressed  , Text of gadget : Stop */
#ifdef KS30
          if (1==GT_GetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Selected,&num,TAG_DONE))
          {
            if (num>=0)
            {
              StopNode(num);
            }
          }
#else
          StopNode(LastNodeClicked);
#endif
          break;
        case Ctrl_Start :
          /* Button pressed  , Text of gadget : Start */
#ifdef KS30
          if (1==GT_GetGadgetAttrs(CtrlGadgets[Ctrl_LV2_Nodes],Ctrl,NULL,GTLV_Selected,&num,TAG_DONE))
          {
            if (num>=0)
            {
              StartNode(num);
            }
          }
#else
          StartNode(LastNodeClicked);
#endif
          break;
        case Ctrl_Config : // node config..
          /* Button pressed  , Text of gadget : Config */

          tempnode=HBBS_NodeDataPtr(LastNodeClicked);
          sprintf(filename,"%sNodeLocal",tempnode->NodeLocation);
          strcpy(tmpstr,BBSGlobal->EditorCMD);
          replace(tmpstr,tmpstr,"{FILE}",filename);
          HBBS_RunDOSCMD(tmpstr,TRUE);

          break;
        case Ctrl_Configure : // open config window..
          if (!CfgOpen)
          {
            if (OpenCtrlCfgWindow()==0)
            {
              CfgOpen=TRUE;
            }
          }
          /* Button pressed  , Text of gadget : Configure */
          break;
        case Ctrl_Shutdown :
          /* Button pressed  , Text of gadget : Shutdown All */
          StopAllNodes();
          break;
        case Ctrl_LV3_Commands :
          if (CheckDoubleClick(MouseX,MouseY,Seconds,Micros))
          {
            strcpy(tmpstr,HBBS_ListName(BBSGlobal->ButtonCMD,Code));
            if ((tmpstr[0]) && (stricmp(tmpstr,"SEPERATOR")!=0))
            {
              HBBS_RunDOSCMD(tmpstr,TRUE);
            }
            // HandleButtonCMD(Code);
          }
          break;
      }
      break;
    case IDCMP_CLOSEWINDOW :
      /* CloseWindow Now */
      // Stop All The Nodes..
      StopAllNodes();
      if (AllNodesShut()) done=1;
      break;
    case IDCMP_REFRESHWINDOW :
      GT_BeginRefresh( Ctrl);
      /* Refresh window. */
      RendWindowCtrl( Ctrl, CtrlVisualInfo );
      GT_EndRefresh( Ctrl, TRUE);
      GT_RefreshWindow( Ctrl, NULL);
      RefreshGList( CtrlGList, Ctrl, NULL, ~0);
      break;
    case IDCMP_VANILLAKEY :
      printf("V %d %c\n",Code,Code);
      break;
    case IDCMP_RAWKEY :
      printf("R %d %c\n",Code,Code);
      break;
  }
  return(done);
}

void ProcessWindowCtrlCfg( LONG Class, UWORD Code, APTR IAddress )
{
struct Gadget *gad;
char tmpstr[1024];

switch ( Class )
  {
  case IDCMP_GADGETUP :
    /* Gadget message, gadget = gad. */
    gad = (struct Gadget *)IAddress;
    switch ( gad->GadgetID )
      {
      case CtrlCfg_ScreenMode :
        /* Button pressed  , Text of gadget : Pick Mode */
        ChangeScreenMode();
        break;
      case CtrlCfg_EditBBSConfig :
        strcpy(tmpstr,BBSGlobal->EditorCMD);
        replace(tmpstr,tmpstr,"{FILE}","HBBS:BBSGlobal");
        HBBS_RunDOSCMD(tmpstr,TRUE);
        break;
      case CtrlCfg_Save :
        /* Button pressed  , Text of gadget : Save */
        SaveScreenPrefs();
        break;
      }
    break;
  case IDCMP_CLOSEWINDOW :
    /* CloseWindow Now */
    CloseCtrlCfgWindow();
    CfgOpen=FALSE;
    break;
  case IDCMP_REFRESHWINDOW :
    GT_BeginRefresh( CtrlCfg);
    /* Refresh window. */
  RendWindowCtrlCfg( CtrlCfg, CtrlCfgVisualInfo );
    GT_EndRefresh( CtrlCfg, TRUE);
  GT_RefreshWindow( CtrlCfg, NULL);
  RefreshGList( CtrlCfgGList, CtrlCfg, NULL, ~0);
    break;
  }
}

void mainloop( void )
{
  int done=0;
  ULONG class;
  WORD mousex,mousey;
  LONG secs,mics;
  UWORD code;
  struct Gadget *pgsel;
  struct IntuiMessage *imsg;
  ULONG ReturnedSig,CfgSig;

  while(done==0)
  {
    CfgSig=CfgOpen ? 1L <<CtrlCfg->UserPort->mp_SigBit : 0L;

    ReturnedSig=Wait(1L << Ctrl->UserPort->mp_SigBit |  1L << BBSGlobal->CtrlMainPort->mp_SigBit | CfgSig);

    if (ReturnedSig & 1L << Ctrl->UserPort->mp_SigBit)
    {
      while (imsg=GT_GetIMsg(Ctrl->UserPort))
      {
        // Copy the bits we need..
        class=imsg->Class;
        code=imsg->Code;
        mousex=imsg->MouseX;
        mousey=imsg->MouseY;
        secs=imsg->Seconds;
        mics=imsg->Micros;
        pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
        GT_ReplyIMsg(imsg);

        done=ProcessWindowCtrl(class, code, pgsel,mousex,mousey,secs,mics);
      }
    }
    if ((CfgOpen) && (ReturnedSig & CfgSig))
    {
      while ((CfgOpen) && (imsg=GT_GetIMsg(CtrlCfg->UserPort)))
      {
        class=imsg->Class;
        code=imsg->Code;
        pgsel=(struct Gadget *)imsg->IAddress;
        GT_ReplyIMsg(imsg);

        ProcessWindowCtrlCfg(class, code, pgsel);
      }
    }

    if (ReturnedSig & 1L << BBSGlobal->CtrlMainPort->mp_SigBit)
    {
      HandleMsg();
    }
  }
}


void CtrlMain(void)
{
  if (OpenTheScreen())
  {
    StartNodes();
    CloseLoadingWindow();
    mainloop();
    CloseTheScreen();
  }
}

V_BOOL CreateNodeList(void)
{
  short loop;
  struct NodeData *node;
  V_BOOL retval=TRUE;
  struct CfgFileData *NodeListCFG;
  UBYTE *option;

  if (NodeListCFG=HBBS_LoadConfig(FILE_NODELIST,LCFG_NONE))
  {

    if (BBSGlobal->NodeList=HBBS_CreateList())
    {
      for (loop=0;loop<BBSGlobal->BBSNodes && retval==TRUE;loop++)
      {
        retval=FALSE;
        // note use of MEMF_CLEAR flag, this will set ALL pointers to NULL so we don't have
        // to do any more work setting up stuff like node->NodeLocation=NULL;

        if (node=(struct NodeData*)AllocVec(sizeof(struct NodeData),MEMF_PUBLIC|MEMF_CLEAR))
        {
          if (node->node.ln_Name=AllocVec(LEN_NODESTATNAME,MEMF_PUBLIC))
          {
            sprintf(node->node.ln_Name,"Node_%d",loop);
            sprintf(node->PortName,"HBBS_Node_%d",loop); // named for door access
            node->NodeNum=loop;

            if (node->DoorList=HBBS_CreateList())
            {
              if (node->TaggedFileList=HBBS_CreateList())
              {

                if (node->CurrentLine=AllocVec(LEN_CURRENTLINE,MEMF_PUBLIC))
                {
                  if (node->CurrentLineWrap=AllocVec(LEN_CURRENTLINE,MEMF_PUBLIC))
                  {
                    if (node->CharsAllowed=AllocVec(256,MEMF_CLEAR))
                    {
                      if (option=AllocVec(strlen(OPT_NODE_Location)+6+1,MEMF_PUBLIC|MEMF_CLEAR))
                      {
                        sprintf(option,"%s%d",OPT_NODE_Location,loop);
                        if (HBBS_GetSetting(NodeListCFG,(void *)&node->NodeLocation,VTYPE_PATH,option,OPT_SINGLE))
                        {
                          retval=TRUE; // temporary usage of variable..
                        } else HBBS_DoErrorMessage(EMSG_NOTENOUGHNL,0,NULL);
                        FreeVec(option);
                      }
                      if ((retval=TRUE) &&(option=AllocVec(strlen(OPT_NODE_AutoStart)+6+1,MEMF_PUBLIC|MEMF_CLEAR)))
                      {
                        retval=FALSE;// end of temportary esage..
                        sprintf(option,"%s%d",OPT_NODE_AutoStart,loop);
                        if (HBBS_GetSetting(NodeListCFG,(void *)&node->AutoStart,VTYPE_BOOL,option,OPT_SINGLE))
                        {
                          // we have to add the node to the list here because
                          // loadnodesettings will not work without it being in the list of nodes.
                          // if we fail to load the settings however, the node is removed from the list again
                          AddTail(BBSGlobal->NodeList,(struct Node*)node);
                          if (LoadNodeSettingsData(loop,&node->NodeSettings)==TYPE_NONE)
                          {
                            retval=TRUE;
                          }
                          else
                          {

                            Remove((struct Node*)node);
                          }
                        } else HBBS_DoErrorMessage(EMSG_NOAUTOSTART,loop,NULL);
                        FreeVec(option);
                      }
                      else retval=FALSE;
                      HBBS_ResetNodeData(node);
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    HBBS_FlushConfig(NodeListCFG);
  } else retval=FALSE;
  return(retval);
}

void FreeNode(struct NodeData *nd)
{
  if (nd->node.ln_Name) FreeVec(nd->node.ln_Name);
  if (nd->CurrentLine) FreeVec(nd->CurrentLine);
  if (nd->CurrentLineWrap) FreeVec(nd->CurrentLineWrap);
  if (nd->CharsAllowed) FreeVec(nd->CharsAllowed);
  if (nd->NodeLocation)
  {
    FreeStr(nd->NodeLocation);
    nd->NodeLocation=NULL;
  }

  if (nd->DoorList)
  {
    PrintList(nd->DoorList); // *C* print them out, there is a bug if any are printed!
    FreeVec(nd->DoorList); // the list should *always* empty at this point
  }
  if (nd->TaggedFileList)
  {
    FreeVec(nd->TaggedFileList);
  }
}

void FreeNodeList(void)
{
  struct NodeData *nd;
  if (BBSGlobal->NodeList)
  {
    while ((nd = (struct NodeData*)BBSGlobal->NodeList->lh_Head) && (nd->node.ln_Succ))
    {
      FreeNode(nd);
      Remove((struct Node *)nd);
      FreeVec(nd);
    }
    FreeVec(BBSGlobal->NodeList);
  }
}

BOOL CreateConfList( void )
{
  short loop;
  struct CfgFileData *ConfListCFG,*ConfCFG;
//  UBYTE *filename;
  struct ConfData *NewConf;
  BOOL error=FALSE;
  BOOL retval=FALSE;
  V_STRINGLIST tmplist=NULL;
  struct Node *tmpnode;
  UBYTE filename[BIG_STR];

  BBSGlobal->ConfList=NULL; // initialise so that FreeConfList can decide wether or not to free the list..

  if (ConfListCFG=HBBS_LoadConfig(FILE_CONFLIST,LCFG_NONE))
  {
    HBBS_GetSetting(ConfListCFG,(void *)&BBSGlobal->NewUserConf,VTYPE_BIGNUM,OPT_CONFL_NewUserConf,OPT_SINGLE);
    HBBS_GetSetting(ConfListCFG,(void *)&BBSGlobal->Conferences,VTYPE_BIGNUM,OPT_CONFL_Conferences,OPT_SINGLE);
    if ((BBSGlobal->Conferences==HBBS_GetSetting(ConfListCFG,(void *)&tmplist,VTYPE_PATHLIST,OPT_CONFL_ConfPath,OPT_MULTI))
       && (BBSGlobal->Conferences>0))
    {

      if (BBSGlobal->ConfList=HBBS_CreateList())
      {
        for (loop=0;loop<BBSGlobal->Conferences && !error ;loop++)
        {
          error=TRUE;
          if (tmpnode=GetNode(tmplist,loop))
          {
            if (NewConf=AllocVec(sizeof(struct ConfData),MEMF_PUBLIC|MEMF_CLEAR)) // WARNING: use MEMF_CLEAR here to initialise pointer to NULL
            {

//** Removed
//              if (filename=AllocVec(strlen(tmpnode->ln_Name)+strlen(FILENAME_CONFCONFIG)+1,MEMF_PUBLIC))
//              {
//**
              strcpy(filename,tmpnode->ln_Name);
              strcat(filename,FILENAME_CONFCONFIG);

              if (ConfCFG=HBBS_LoadConfig(filename,LCFG_NONE))
              {
                NewConf->ConfPath=DupStr(tmpnode->ln_Name);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->node.ln_Name ,VTYPE_STRING     ,OPT_CONF_ConfName      ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->ConfActive   ,VTYPE_BOOL       ,OPT_CONF_ConfActive    ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->ConfAccess   ,VTYPE_SMALLNUM   ,OPT_CONF_ConfAccess    ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->HoldFileList ,VTYPE_STRING     ,OPT_CONF_HoldFileList  ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->HoldFiles    ,VTYPE_PATHLIST   ,OPT_CONF_HoldFiles     ,OPT_MULTI);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->BadFileList  ,VTYPE_STRING     ,OPT_CONF_BadFileList   ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->BadFiles     ,VTYPE_PATHLIST   ,OPT_CONF_BadFiles      ,OPT_MULTI);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->PartUpload      ,VTYPE_PATHLIST   ,OPT_CONF_PartUpload       ,OPT_MULTI);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->Upload       ,VTYPE_PATHLIST   ,OPT_CONF_Upload        ,OPT_MULTI);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->Download     ,VTYPE_PATHLIST   ,OPT_CONF_Download      ,OPT_MULTI);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->MaxDIZLines  ,VTYPE_SMALLNUM   ,OPT_CONF_MaxDIZLines   ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->MenuPrompt   ,VTYPE_STRING     ,OPT_CONF_MenuPrompt    ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->AutoMailScan ,VTYPE_BOOL       ,OPT_CONF_AutoMailScan  ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->ConfPassWD   ,VTYPE_STRING     ,OPT_CONF_ConfPassWD    ,OPT_SINGLE);
                HBBS_GetSetting(ConfCFG,(void *)&NewConf->UserAllowed  ,VTYPE_STRINGLIST ,OPT_CONF_UserAllowed   ,OPT_MULTI);
                NewConf->FileLists=HBBS_GetSetting(ConfCFG,(void *)&NewConf->FileList     ,VTYPE_STRINGLIST ,OPT_CONF_FileList      ,OPT_MULTI);

                // *C* check all requires fields are present!
                AddTail(BBSGlobal->ConfList,(struct Node*)NewConf);
                NewConf->ConfNum=loop+1; // set the conference number
                error=FALSE;

                HBBS_FlushConfig(ConfCFG);
              }

//** Removed
//                FreeVec(filename);
//              }
//**
            }
          }
        }
        retval=!error; // return TRUE if NO error..
      }
    }
    FreeStrList(tmplist);
    HBBS_FlushConfig(ConfListCFG);
  }
  return(retval);
}

void FreeConfList( void )
{
  struct ConfData *Conf;
  if (BBSGlobal->ConfList)
  {
    while (BBSGlobal->ConfList->lh_Head->ln_Succ)
    {
      Conf=(struct ConfData*)BBSGlobal->ConfList->lh_Head;

      // free the data..

      FreeStr(Conf->node.ln_Name);
      FreeStr(Conf->HoldFileList);
      FreeStrList(Conf->HoldFiles);
      FreeStr(Conf->BadFileList);
      FreeStrList(Conf->BadFiles);
      FreeStrList(Conf->PartUpload);
      FreeStrList(Conf->FileList);
      FreeStrList(Conf->Upload);
      FreeStrList(Conf->Download);
      FreeStr(Conf->MenuPrompt);
      FreeStr(Conf->ConfPassWD);
      FreeStrList(Conf->UserAllowed);
      FreeStr(Conf->ConfPath);
      Remove((struct Node*)Conf);
      FreeVec(Conf);
    }
    FreeVec(BBSGlobal->ConfList);
  }
}

BOOL CheckDirs(struct List *list)
{
  struct Node *node;
  BOOL error=FALSE;

  // i COULD put && !error in the loop check, but it's more usefull for the sysop
  // if he/she knows about all the things that are wrong in one go..

  for (node = list->lh_Head ; node->ln_Succ ; node =node->ln_Succ)
  {
    if (node->ln_Name)
    {
      if (!PathOK(node->ln_Name))
      {
        error=TRUE;
        HBBS_DoErrorMessage(EMSG_PATHMISSING,0,node->ln_Name);
      }
    }
  }
  return((BOOL)!error); // return FALSE if the WAS an error..
}

BOOL CheckConfPaths( void )
{
  char *str;
  struct ConfData *Conf;
  short error=0;

  for (Conf = (struct ConfData *)BBSGlobal->ConfList->lh_Head ; Conf->node.ln_Succ && error==0 ; Conf =(struct ConfData *)Conf->node.ln_Succ)
  {
    if (str=AllocVec(strlen(Conf->ConfPath)+LEN_FILENAME_CONF_LONGEST,MEMF_PUBLIC))
    {
      error=2;
      strcpy(str,Conf->ConfPath);
      strcat(str,FILENAME_COMMANDS);
      if (PathOK(str))
      {
        strcpy(str,Conf->ConfPath);
        strcat(str,FILENAME_LOSTFILES);
        if (PathOK(str))
        {
          strcpy(str,Conf->ConfPath);
          strcat(str,FILENAME_ACCESS);
          if (PathOK(str))
          {
            strcpy(str,Conf->ConfPath);
            strcat(str,FILENAME_SCREENS);
            if (PathOK(str))
            {
              error=1;
              if (CheckDirs(Conf->HoldFiles))
              {
                if (CheckDirs(Conf->BadFiles))
                {
                  if (CheckDirs(Conf->PartUpload))
                  {
                    error=0;
                  }
                }
              }
            }
          }
        }
      }
      if (error==2) HBBS_DoErrorMessage(EMSG_CONFPATHSERROR,0,NULL);
      FreeVec(str);
    }
  }
  return((BOOL)(error==0 ? TRUE : FALSE));
}

BOOL LoadLevels( void )
{
  struct CfgFileData *LevelListFile;
  BOOL retval=FALSE;
//  struct Node *node;

  BBSGlobal->AcsLevelList=NULL;
  BBSGlobal->AcsLevelNames=NULL;

  if (LevelListFile=HBBS_LoadConfig(FILE_ACSLEVELLIST,LCFG_NONE))
  {
    if (BBSGlobal->AcsLevels=HBBS_GetSetting(LevelListFile,(void *)&BBSGlobal->AcsLevelList,VTYPE_STRINGLIST,OPT_LLIST_LEVEL,OPT_MULTI))
    {
      if (BBSGlobal->AcsLevels==HBBS_GetSetting(LevelListFile,(void *)&BBSGlobal->AcsLevelNames,VTYPE_STRINGLIST,OPT_LLIST_LEVELNAME,OPT_MULTI))
      {
        // we don't actually load access settings here at all, instead they are loaded for
        // each conference join and every login
        retval=TRUE;
      }
    }
    HBBS_FlushConfig(LevelListFile);
  }
  else HBBS_DoErrorMessage(EMSG_LEVELSFILEERROR,0,NULL);
  return(retval);
}

void FreeLevels( void )
{
  if (BBSGlobal->AcsLevelList) FreeStrList(BBSGlobal->AcsLevelList);
  if (BBSGlobal->AcsLevelNames) FreeStrList(BBSGlobal->AcsLevelNames);
}

BOOL LoadProtocols( void )
{
  struct CfgFileData *ProtocolListFile;
  struct ProtocolNode *pnode;
  BOOL error=FALSE;
  char *tmpstr=NULL;
  char optionname[BIG_STR]="Name_1";
  int num=1;


  // Load Config File..

  if (ProtocolListFile=HBBS_LoadConfig(FILE_PROTOCOLLIST,LCFG_NONE))
  {
    // Allocate List..

    if (BBSGlobal->ProtocolList=HBBS_CreateList())
    {

      // Find Protocol Info..

      while ((!error) && (HBBS_GetSetting(ProtocolListFile,(void *)&tmpstr,VTYPE_STRING,optionname,OPT_SINGLE)))
      {
        error=TRUE;

        // Allocate Protocol Node and get the rest of the settings..

        if (pnode=AllocVec(sizeof(struct ProtocolNode),MEMF_PUBLIC|MEMF_CLEAR)) // to NULL all variables!
        {
          if (pnode->node.ln_Name=DupStr(tmpstr))
          {
            FreeStr(tmpstr);
            tmpstr=NULL;
            // *C* add checking for missing items!

            sprintf(optionname,"Type_%d",num);
            HBBS_GetSetting(ProtocolListFile,(void *)&tmpstr,VTYPE_STRING,optionname,OPT_SINGLE);
            if (stricmp(tmpstr,"UNIDIRECTIONAL")==0) pnode->protocoltype=PTYPE_UNIDIRECTIONAL;
            if (stricmp(tmpstr,"BIDIRECTIONAL")==0) pnode->protocoltype=PTYPE_BIDIRECTIONAL;
            FreeStr(tmpstr);
            tmpstr=NULL;

            sprintf(optionname,"ModuleName_%d",num);
            HBBS_GetSetting(ProtocolListFile,(void *)&pnode->modulename,VTYPE_STRING,optionname,OPT_SINGLE);

            sprintf(optionname,"ModuleOpts_%d",num);
            if (HBBS_GetSetting(ProtocolListFile,(void *)&pnode->moduleopts,VTYPE_STRING,optionname,OPT_SINGLE)==0)
            {
              if (pnode->moduleopts=AllocVec(1,MEMF_PUBLIC))
              {
                pnode->moduleopts[0]=0; // Null Terminated String so that string output
                                        // routines still work..
              }
            }

            sprintf(optionname,"AllowUL_%d",num);
            HBBS_GetSetting(ProtocolListFile,(void *)&pnode->allow_ul,VTYPE_BOOL,optionname,OPT_SINGLE);

            sprintf(optionname,"AllowDL_%d",num);
            HBBS_GetSetting(ProtocolListFile,(void *)&pnode->allow_dl,VTYPE_BOOL,optionname,OPT_SINGLE);

            sprintf(optionname,"AllowBatch_%d",num);
            HBBS_GetSetting(ProtocolListFile,(void *)&pnode->allow_batch,VTYPE_BOOL,optionname,OPT_SINGLE);

            // Add Completed protocol node to the list of protocols

            AddTail(BBSGlobal->ProtocolList,(struct Node *)pnode);

            num++;
            sprintf(optionname,"Name_%d",num+1);
            error=FALSE;
          }
        }


      }
      // free temp string..

      FreeStr(tmpstr);

    }

    // free config..

    HBBS_FlushConfig(ProtocolListFile);
  }
  else HBBS_DoErrorMessage(EMSG_PROTOCOLSFILEERROR,0,NULL);

  // Set the amount of protocols loaded

  BBSGlobal->Protocols=num-1;

  // if no protocols were loaded the return false!
  return((BOOL)(BBSGlobal->Protocols ? TRUE : FALSE));
}

void FreeProtocols( void )
{
  struct ProtocolNode *pnode;

  if (BBSGlobal->ProtocolList)
  {
    while (BBSGlobal->ProtocolList->lh_Head->ln_Succ)
    {
      pnode=(struct ProtocolNode *)BBSGlobal->ProtocolList->lh_Head;
      FreeStr(pnode->node.ln_Name);
      FreeStr(pnode->modulename);
      FreeStr(pnode->moduleopts);
      Remove((struct Node *)pnode);
      FreeVec(pnode);
    }
    FreeVec(BBSGlobal->ProtocolList);
  }

}

BOOL CheckBBSGlobal( void )
{
  BOOL retval=TRUE;
  if (BBSGlobal->BBSName==NULL ||
      BBSGlobal->BBSNodes==0 ||
      BBSGlobal->Drive==NULL ||
      BBSGlobal->BBSDrive==NULL)
  {
    HBBS_DoErrorMessage(EMSG_BBSGLOBALERR,0,NULL);
    retval=FALSE;
  }

  if (HBBS_NodesInList(BBSGlobal->ButtonName)!=HBBS_NodesInList(BBSGlobal->ButtonCMD))
  {
    FreeStrList(BBSGlobal->ButtonName);
    BBSGlobal->ButtonName=NULL;
    FreeStrList(BBSGlobal->ButtonCMD);
    BBSGlobal->ButtonCMD=NULL;

  }
  return(retval);
}

BOOL InitGlobal( void )
{
  if (LoadBBSGlobalData(FILE_BBSGLOBAL,BBSGlobal)==TYPE_NONE)
  {
    if (LoadScreenPrefs())
    {
      // *C* check required setting.. also need to check for valid assigns on the drives..
      // must also check config pairs match up.. (e.g. LanguageName/Extn)
      if (CheckBBSGlobal())
      {
        if (HBBS_FindTotalUsers()>=0)
        {
          if (LoadLevels())
          {
            if (LoadProtocols())
            {
              return(TRUE);
            }
          }
        }
      }
    }
  }
  return(FALSE);
}

void FreeGlobal( void )
{
  FreeProtocols();
  FreeLevels();
  FreeBBSGlobalData(BBSGlobal);
}

BOOL InitNodes( void )
{
  // Read Settings to be used as a default for all nodes.
  if (LoadNodeSettingsData(NODE_GLOBAL,&BBSGlobal->NodeGlobalData->NodeSettings)==TYPE_NONE)
  {
    // create list of nodes..
    if (CreateNodeList())
    {
      // *C* we need to check that all the required settings for the node have been
      // set and to set other values to defaults..
      return(TRUE);
    }
  }
  FreeNodeSettingsData(&BBSGlobal->NodeGlobalData->NodeSettings);

  return(FALSE);
}

BOOL InitConferences( void )
{
  if (CreateConfList())
  {
    if (CheckConfPaths()) return(TRUE);
  }
  return(FALSE);
}

void FreeConferences( void )
{
  FreeConfList();
}

int main(int argc,char **argv)
{
  WB2CLI((struct WBStartup *)argv,4000,DOSBase);  /* so you get the default path! */
  CtrlProc=(struct Process *)FindTask (NULL); // can this fail ?? :-)
  init();

  OpenLoadingWindow();
  if (AssignOK("HBBS:"))
  {
    if (InitGlobal())
    {
      if (InitNodes())
      {
        HBBS_GetDate(BBSGlobal->LastCalledDate);
        if (InitConferences())
        {
          if (LoadPrivateData())
          {
            HBBS_LoadCallsData();
            CtrlMain();
            HBBS_SaveCallsData();
          }
        }
      //  HBBS_rterror("Freeing Conferences");
        FreeConferences();
    //    HBBS_rterror("Freeing Node Settings Data");
        FreeNodeSettingsData(&BBSGlobal->NodeGlobalData->NodeSettings);
      }
  //    HBBS_rterror("Freeing Nodes");
      FreeNodeList();
    }
//    HBBS_rterror("Freeing Global");
    FreeGlobal(); // we call FreeGlobal even if InitGlobal fails..
  }
  else
  HBBS_DoErrorMessage(EMSG_NOHBBS,0,NULL);
//  HBBS_rterror("Cleanup");
  HBBS_SetBBS(NULL); // so that if the lib is still in memory utils won't work anymore..
  cleanup(NULL);
}
