/*

   Node_Input.c - written By Dominic Clifton, part of HydraBBS's Node program

   (C) 1994-7 Dominic Clifton - Hydra/dAT - Deluxe Software


   Primarly the code for checking input from the user/sysop and
   handles door<->node communications.

*/

//#define USE_BUILTIN_MATH
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <exec/types.h>
#include <libraries/locale.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <diskfont/diskfont.h>
#include <utility/utility.h>
#include <graphics/gfxbase.h>
#include <devices/console.h>
#include <workbench/workbench.h>
#include <graphics/scale.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 <dos/dos.h>
#include <dos/dostags.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <libraries/reqtools.h>

#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/reqtools_protos.h>

#include "NodeGUI.h"

#define ClrSignal(s)  SetSignal(0,s)

extern struct ReqToolsBase *ReqToolsBase;
extern ULONG InfoWinActive;
extern ULONG *rttags;

#include <hbbs/types.h>
#include <hbbs/errors.h>
#include <hbbs/access.h>
#include <hbbs/defines.h>
#include <hbbs/structures.h>
#include <hbbs/strings.h>
#include <hbbs/files.h>
#include <hbbs/ansi_codes.h>

#include <HBBS/Hbbscommon_protos.h>
#ifdef __SASC
#include <HBBS/hbbscommon_pragmas_sas.h>
#else
#include <HBBS/hbbscommon_pragmas_stc.h>
#endif

#include <HBBS/Hbbsnode_protos.h>
#ifdef __SASC
#include <HBBS/Hbbsnode_pragmas_sas.h>
#else
#include <HBBS/Hbbsnode_pragmas_stc.h>
#endif

//#include "Node_Console_Protos.h"
#include "Node_Serial_Protos.h"
#include "Node_Main_Protos.h"
#include "Node_Misc_Protos.h"
#include "Node_Input_Protos.h"

extern int N_NodeNum;
extern struct BBSGlobalData *BBSGlobal;
extern struct NodeData *N_ND;
extern struct Library *HBBSCommonBase;
extern struct Library *HBBSNodeBase;
extern struct List *HistoryList;
extern ULONG HistoryItems;

#include "/common/shared_protos.h"

void SetUpSigs( void )
{
  // this routine sets up all the signals availiable so you can checksignal() them
  // or wait() on them or whatever....


  if (N_ND->NodeSettings.Iconified==FALSE)
    N_ND->WinSig=DEF_WINSIG;
  else
    N_ND->WinSig=0L;

  if (N_ND->InformationOpen)
    N_ND->InfoWinSig=DEF_INFOWINSIG;
  else
    N_ND->InfoWinSig=0L;

  if (N_ND->SettingsOpen)
    N_ND->SettingsWinSig=DEF_SETTINGSWINSIG;
  else
    N_ND->SettingsWinSig=0L;

  SetupConSerSigs();

  N_ND->PortSig=DEF_PORTSIG;
  N_ND->TimerSig=DEF_TIMERSIG;
}

void SetUpDoorSigs( void )
{
  // this routine sets up all the signals availiable so you can checksignal() them
  // or wait() on them or whatever....

  if (N_ND->NodeSettings.Iconified==FALSE)
    N_ND->WinSig=DEF_WINSIG;
  else
    N_ND->WinSig=0L;

  if (N_ND->InformationOpen)
    N_ND->InfoWinSig=DEF_INFOWINSIG;
  else
    N_ND->InfoWinSig=0L;

  if (N_ND->SettingsOpen)
    N_ND->SettingsWinSig=DEF_SETTINGSWINSIG;
  else
    N_ND->SettingsWinSig=0L;

  N_ND->PortSig=DEF_PORTSIG;
  N_ND->TimerSig=DEF_TIMERSIG;
}


void HandleMiscSigs( ULONG ReturnedSigs )
{
  // this routine handles all stuff like the sysop clicking on the node window
  // or closing the watch screen etc. etc...

  ULONG class;
  UWORD code;
  struct Gadget *pgsel;
  struct IntuiMessage *imsg;

  if (ReturnedSigs & N_ND->WinSig)
  {
    while ((NodeWnd) && (imsg=GT_GetIMsg(NodeWnd->UserPort)))
    {
      class=imsg->Class;
      code=imsg->Code;
      pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
      GT_ReplyIMsg(imsg);
      ProcessWindowNodeWnd(class, code, pgsel);
    }
  }

  if (ReturnedSigs & N_ND->ConWinSig)
  {
    while ((N_ND->ConWin) && (imsg=GT_GetIMsg(N_ND->ConWin->UserPort)))
    {
      class=imsg->Class;
      code=imsg->Code;
      pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
      GT_ReplyIMsg(imsg);
      ProcessConWindow(class, code, pgsel);
    }
  }

  if (ReturnedSigs & N_ND->InfoWinSig)
  {
    while ((InfoWin) && (imsg=GT_GetIMsg(InfoWin->UserPort)))
    {
      class=imsg->Class;
      code=imsg->Code;
      pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
      GT_ReplyIMsg(imsg);
      ProcessWindowInfoWin(class, code, pgsel);
    }
  }

  if (ReturnedSigs & N_ND->SettingsWinSig)
  {
    while ((SettingsWin) && (imsg=GT_GetIMsg(SettingsWin->UserPort)))
    {
      class=imsg->Class;
      code=imsg->Code;
      pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
      GT_ReplyIMsg(imsg);
      ProcessWindowSettingsWin(class, code, pgsel);
    }
  }

  if (ReturnedSigs & N_ND->PortSig)
  {
    HandleMsg(); // node can only be closed by a message from the CONTROL program
  }

  if (ReturnedSigs & (1L << N_ND->OLMPort->mp_SigBit))
  {
    struct Message *Msg;

    while (Msg=GetMsg(N_ND->OLMPort))
    {
      FreeVec(Msg);
    }
  }
}

ULONG WaitAllSigs(void)
{
  return(Wait((1L << N_ND->OLMPort->mp_SigBit) | N_ND->SettingsWinSig | N_ND->InfoWinSig |N_ND->WinSig | N_ND->PortSig | N_ND->ConSig | N_ND->SerSig | N_ND->ConWinSig | N_ND->TimerSig));
}



ULONG CheckCSI(UBYTE ch,ULONG wherefrom)
{
  ULONG ReturnedSigs;
  BOOL done=FALSE;
  int csipos=0;
//  int loop;

  if ((N_ND->char1==27 && ch=='[') || (ch==155))
  {

    N_ND->char1=0;
    while (!done)
    {
      // now, we have to be specific as to where we think the control sequence
      // is coming from, for instance, say you had got the first byte of the
      // control sequence from the serial port, you obviously wouldn't want
      // any more of the control sequence coming from the console.. :-)

      if (wherefrom==GET_SERIAL)
      {
        if (!N_ND->SerWaiting) SendSerReadData();
      }
      if (wherefrom==GET_CONSOLE)
      {
        if (!N_ND->ConWaiting && N_ND->ConOK) ConReadData(1); // we only want to do a char at a time...
      }
      SetUpSigs();
      ReturnedSigs=WaitAllSigs();
      // handlemiscsigs() MAY close the console window.. so be careful!
      // also N_ND->RequestShutdown MIGHT get set if the sysop clicks close
      // on the control panel, BUT if we are in the middle of an escape sequence
      // we don't want to know.
      HandleMiscSigs(ReturnedSigs);

      while ( ((wherefrom==GET_SERIAL) && HandleSerSigs(ReturnedSigs)) ||
              ((wherefrom==GET_CONSOLE) && HandleConSigs(ReturnedSigs)) )
      {
        if ( N_ND->IBuffer[0]>=0x40 && N_ND->IBuffer[0] <=0x7E)
        {
          N_ND->csistring[csipos]=N_ND->IBuffer[0];
          csipos++;
          done=TRUE;
        }
        else
        {
          N_ND->csistring[csipos]=N_ND->IBuffer[0];
          csipos++;
        }
      }
    }
    N_ND->csistring[csipos]=0; // null terminate str..

    // note: csipos is same as doing strlen(N_ND->csistring)..

    return(KEY_CSI);

  }
  else
  {
    if (ch==27)
    {
      N_ND->char1=27;
      return(KEY_NONE);
    }
  }

  if (N_ND->char1==27)
  {
    N_ND->char1=0;
    return(KEY_ESCAPE);
  }
  return(KEY_NONE);
}

void handleescape( void )
{
//  puts("Escape Pressed!");
}

// when writing data to the console or serial port we MUST use <ESC>[ and not 0x9B
// cos then only amiga's would understand it! :-)  But hey! what a cool idea!

/* NOT USED ANYMORE!  only getline used them and I've had to modify that too much!
  void CursorLeft( short num )
  {
    UBYTE outstr[6]; // csi+3digits+ansi terminator+null terminator
    sprintf(outstr,"\033[%dD",num);
    PutText(outstr);
  }

  void CursorRight( short num )
  {
    UBYTE outstr[6]; // csi+3digits+ansi terminator+null terminator
    sprintf(outstr,"\033[%dC",num);
    PutText(outstr);
  }

  void DeleteChars( short num )
  {
    UBYTE outstr[6]; // csi+3digits+ansi terminator+null terminator
    sprintf(outstr,"\033[%dP",num);
    PutText(outstr);
  }

  void InsertChars( short num )
  {
    UBYTE outstr[6]; // csi+3digits+ansi terminator+null terminator
    sprintf(outstr,"\033[%d@",num);
    PutText(outstr);
  }
*/

ULONG CheckFunctionKeys( void )
{
  // this routine checks the function keys, and depending on the
  // N_ND->LoginType it does certain things...

  ULONG status=IN_NOTHING;

//  puts(N_ND->csistring);

  if (stricmp(N_ND->csistring,str_CSI_F7)==0)
  {
    HBBS_CleanupNodeConsoleWin();
  }
  else
  if (stricmp(N_ND->csistring,str_CSI_F8)==0)
  {
    HBBS_ChangeConsoleMode(2); // swap between screen and window..
    if ((N_ND->LoginType==LOGIN_NONE) && (N_ND->CurrentLine[0]==0)) status=IN_DISPLAYAWAIT;
  }
  else
  if (N_ND->LoginType==LOGIN_NONE)
  {
    if (stricmp(N_ND->csistring,str_CSI_F1)==0) status=IN_LOGIN;      // Local Login
    if (stricmp(N_ND->csistring,str_CSI_F2)==0)
    {
      InitModem();          // Init Modem
    }
    if (stricmp(N_ND->csistring,str_CSI_F3)==0) status=IN_IMMEDIATE;  // Answer Modem Now
    if (stricmp(N_ND->csistring,str_CSI_F4)==0) status=IN_DISPLAYAWAIT; // re-display Awaitscreen
    if (stricmp(N_ND->csistring,str_CSI_F5)==0)                       // toggle modem debug
    {
      N_ND->NodeDevice.ModemDebug=!N_ND->NodeDevice.ModemDebug;
    }
    if (stricmp(N_ND->csistring,str_CSI_F6)==0)
    {
      // we have to fool the system into thinking that there's someone
      // logged in so that we can run the door! :-)
      N_ND->LoginType=LOGIN_LOCAL;
      N_ND->OnlineStatus=OS_ONLINE;

      GoSystemDoor("ACCOUNTEDIT",NULL);

      // but not forgetting to put it back to how it was..
      N_ND->LoginType=LOGIN_NONE;
      N_ND->OnlineStatus=OS_OFFLINE;

      // and display the await screen again to tidy up..
      status=IN_DISPLAYAWAIT;
    }
    if (stricmp(N_ND->csistring,str_CSI_F9)==0) status=IN_TERMINAL;
    if (stricmp(N_ND->csistring,str_CSI_SF9)==0) status=IN_DIALOUT;
    if (stricmp(N_ND->csistring,str_CSI_F10)==0) status=IN_SHUTDOWN;
  }
  else // Oooh someone's logged in and the sysop wants to do something to the user!
  {
    if (stricmp(N_ND->csistring,str_CSI_F1)==0)
    {
      // chat door running ?
      if ((N_ND->ActiveDoor) && (iposition("SYSOPCHAT",N_ND->ActiveDoor->node.ln_Name)>=0))
      {
        status=IN_ENDCHAT;
      }
      else
      {
        GoSystemDoor("SYSOPCHAT",NULL);
        status=IN_PAGEANSWERED;
      }
    }
    else
    if (stricmp(N_ND->csistring,str_CSI_F6)==0)
    {
      if ((N_ND->ActiveDoor) && (iposition("ACCOUNTEDIT",N_ND->ActiveDoor->node.ln_Name)<0))
      {
        GoSystemDoor("ACCOUNTEDIT","SYSOPONLY");
      }
    }
    else
    if (stricmp(N_ND->csistring,str_CSI_SF6)==0)
    {
      if ((N_ND->ActiveDoor) && (iposition("ACCOUNTEDIT",N_ND->ActiveDoor->node.ln_Name)<0))
      {
        GoSystemDoor("ACCOUNTEDIT","SYSOPONLY CALLONLY");
      }
    }
    else
    if (stricmp(N_ND->csistring,str_CSI_SF10)==0)
    {
      if (N_ND->LoginType==LOGIN_REMOTE) HangUp();
      return(IN_LOSSCARRIER);
    }
  }
  return(status);
}
/*  see defines.h

#define GL_NONE      0  // default
#define GL_HISTORY   1  // if set history is enabled (up/down cursor keys)
#define GL_LINEWRAP  2  // if maxlen is reached then data back to the last ' ' character is copied to N_ND->CurrentLineWrap and returns immediately
#define GL_IMMEDIATE 4  // returns when maxlen is reached
#define GL_NORETURN  8  // does not print a cr+lf when a) reached maxlen or b) timeout or c) return press
#define GL_EDIT      16 // enables cursor left/right and backspace/delete
#define GL_DISPLAY   32 // display chars (don't specify for "Press [return] to contine" type prompts)
#define GL_SYSOP     64 // does not write to, or read from, the serial port
#define GL_USECHARS  128 // see N_ND->CharsAllowed
#define GL_NOBEEP    256 // use with GL_USECHARS if you don't want a beep when
                         // the presses a key not in N_ND->CharsAllowed
#define GL_NOOLM     512 // (No OLM) if set olm's will not inturrpt the user
#define GL_NODISTURB 1024// Lets you use things like "Enter ID: [    ]" with out the ] moving
#define GL_CVTUPPER  2048// converts input chars to uppercase as the keys are pressed!

*/

void ReDrawLine(ULONG Flags, int *xpos)
{
  char cmovestr[20];

  if (Flags & GL_DISPLAY)
  {
    // now move back to the start of the string and erase it,
    // then print the new string

    if (*xpos)
    {
      sprintf(cmovestr,"\033[%dD",*xpos);
    }
    if (Flags & GL_SYSOP)
    {
      if (*xpos) PutConText(cmovestr);
      PutConText("\033[K");
      PutConText(N_ND->CurrentLine);
    }
    else
    {
      if (*xpos) PutText(cmovestr);
      PutText("\033[K");
      PutText(N_ND->CurrentLine);
    }
  }
  *xpos=strlen(N_ND->CurrentLine);
}


V_BOOL CheckRaw(ULONG Flags)
{
  V_BOOL GotSomeData=FALSE;

  N_ND->CurrentLine[0]=0;
  N_ND->CurrentLine[1]=0;
  N_ND->CurrentLine[2]=0;
  N_ND->CurrentLine[3]=IN_GOTLINE;
  if (!(Flags & CR_NOCONSOLE) && (N_ND->ConOK))    // fixed, 25/07/96
  {
    if (N_ND->ConWaiting) AbortConRead();
    N_ND->ConRead->io_Command  = CMD_READ;
    N_ND->ConRead->io_Data     = (APTR)N_ND->CurrentLine;
    N_ND->ConRead->io_Length   = 1;

    ClrSignal(1L << N_ND->ConRPort->mp_SigBit);
    SendIO((struct IORequest*)N_ND->ConRead);

    if (CheckIO((struct IORequest*)N_ND->ConRead))
    {
      WaitIO((struct IORequest*)N_ND->ConRead);
      if (N_ND->ConRead->io_Actual)
      {
        GotSomeData=TRUE;
        N_ND->CurrentLine[2]=GET_CONSOLE;

        if (Flags & CR_CHECKFUNC)
        {
          if (KEY_CSI == CheckCSI(N_ND->CurrentLine[0],GET_CONSOLE))
          {
            N_ND->CurrentLine[3]=(char)CheckFunctionKeys();
          }
        }
      }
    }
    else
    {
      AbortIO((struct IORequest*)N_ND->ConRead);
    }
  }
  if (!((GotSomeData) || (Flags & CR_NOSERIAL)) && (N_ND->SerOK)) // fixed, 25/07/96
  {
    if (SerQueryData()>0)
    {
      WaitSerReadBlock(N_ND->CurrentLine,1);
      GotSomeData=TRUE;
      N_ND->CurrentLine[2]=GET_SERIAL;

      if (Flags & CR_CHECKFUNC)
      {
        if (KEY_CSI == CheckCSI(N_ND->CurrentLine[0],GET_SERIAL))
        {
          N_ND->CurrentLine[3]=(char)CheckFunctionKeys();
        }
      }
    }
  }
  if (!GotSomeData) N_ND->CurrentLine[3]=IN_TIMEOUT; // not really a timeout. but is usefully set
  return((V_BOOL)GotSomeData);
}

ULONG Get_Line(ULONG Flags,char PasswordChar,ULONG MaxLen, ULONG Timeout, UBYTE *PromptStr)
{
  char tmpstr[BIG_STR];
  int editloop,editlen;
  ULONG ReturnedSigs;
  UBYTE CurrentChar[2]={0,0}; // 2 chars, use &currentchar for null terminated string type..
  int xpos=0;
  ULONG csitype=0;
  ULONG retval=IN_NOTHING; // this must NOT be returned to the caller!!!!!
  struct TimerData *TD=NULL,    // TD is used if a timeout is specified
                   *TO_TD=NULL, // used for Inactivity Disconnect
                   *TL_TD=NULL; // used for time remaining


  BOOL AddOK;
  char *strptr;

  LONG HistoryNum=HistoryItems,OldHistNum=-1;
  char cmovestr[20];

  if (N_ND->NodeFlags & NFLG_CTRLC) N_ND->NodeFlags-=NFLG_CTRLC;


  N_ND->CurrentLine[0]=0; // null terminate the string..
  N_ND->CurrentLineWrap[0]=0; // null terminate the string..

  if (MaxLen==0) MaxLen=LEN_CURRENTLINE;

  if (PromptStr)
  {
    strNcpy(N_ND->CurrentLine,PromptStr,MaxLen);

    if (PasswordChar) // Convert promptstr to a blanked out string..
    {
      for (editloop=0;N_ND->CurrentLine[editloop];editloop++)
      {
        if (Flags & GL_SYSOP)
        {
          PutConChar(PasswordChar);
        }
        else
        {
          PutChar(PasswordChar);
        }
      }
    }
    else
    {
      if (Flags & GL_SYSOP)
      {
        PutConText(N_ND->CurrentLine);
      }
      else
      {
        PutText(N_ND->CurrentLine);
      }
    }
    xpos=strlen(N_ND->CurrentLine);
  }

  if ((N_ND->LoginType==LOGIN_REMOTE) && (CarrierLost())) retval=IN_LOSSCARRIER;

  while (retval==IN_NOTHING)
  {
    // we only want to read the serial port if a) it's open and b) if we're
    // still awaiting connect or someone is logged on..

    if (N_ND->SerOK && N_ND->LoginType !=LOGIN_LOCAL && (!(Flags & GL_SYSOP))  && (!(N_ND->NodeFlags & NFLG_BLOCKSERIAL)))
    {
      if (!N_ND->SerWaiting) SendSerReadData();
    }

    // we ALWAYS want to read the console if the console window is open.
    // cos the sysop might wanna do sommat!

    if (!N_ND->ConWaiting && N_ND->ConOK) ConReadData(1);

    // Submit Timer for Inactivity Disconnect..


    if (!N_ND->NodeSettings.InactivityDisconnectOverride)
    {
      if (!(Flags & GL_NOINACTIVITY))
      {
        if (N_ND->User.Valid && !N_ND->RunningAwait)
        {
          TO_TD=SubmitTimer(N_ND->NodeTimer,N_ND->NodeSettings.InactivitySeconds,0);
        }
      }
    }

    // Submit Timer for time allowed to enter string without chars being pressed.

    if (Timeout)
    {
      TD=SubmitTimer(N_ND->NodeTimer,Timeout,0);
    }

    // Submit Timer for time allowed left online

    if (N_ND->User.Valid && !N_ND->RunningAwait)
    {
      if (N_ND->User.Acs.Data[ACS_UNLIMTIME]=='N' && HBBS_TimeLeft()+1>0)
      {
        TL_TD=SubmitTimer(N_ND->NodeTimer,(HBBS_TimeLeft()+1) * 60,0);
      }
    }


    SetUpSigs();

    ClrSignal(N_ND->TimerSig);  // if we don't do this we keep getting signals when nothing has happened.
                                // note: it took me ages to find this.... Argh!  The symptom was that all the
                                // cpu time disappeared when the bbs was waiting for input :-)! The complete
                                // opposite of normal operation

    ReturnedSigs=WaitAllSigs();
    HandleMiscSigs(ReturnedSigs);

    // check olm's ?  *C* add an option/flag to NOT check for OLM's..

    if ((!(N_ND->NodeFlags & NFLG_HANDLINGOLM)) && (!(Flags & GL_NOOLM)) && (ReturnedSigs & (1L << N_ND->OLMPort->mp_SigBit)) && (N_ND->NodeFlags & NFLG_OLMSWAITING))
    {
      GoSystemDoor("ReadOLM",NULL);

      xpos=0;
      ReDrawLine(Flags,&xpos);
    }

    if (TO_TD)
    {
      if (CheckTimer(N_ND->NodeTimer,TO_TD))
      {
        TO_TD=NULL;
        PutText("\r\n\r\nInactivityTimeout!\r\n\r\n"); //*C* make configurable, add option for executing door.

        GoSystemDoor("InactivityTimeout",NULL);

        sprintf(tmpstr,"Inactivity Timeout after %d seconds",N_ND->NodeSettings.InactivitySeconds);
        HBBS_AddToCallersLog(tmpstr);

        N_ND->Actions[ACTN_INACTIVITYTIMEOUT]=ACTC_INACTIVITYTIMEOUT;

        // do stuff needed for sucessful hanging up of phone (actually this just sets the
        // os_onlinestatus, the door must check this...

//        DOOR_Goodbye();
//        HangUp();

//        N_ND->OnlineStatus=OS_OFFLINE;


        retval=IN_LOSSCARRIER;
      }
      else
      {

        // If we have an inactivity timer then we need to restart the timer
        // as a key has been pressed

        AbortTimer(N_ND->NodeTimer,TO_TD);
        TO_TD=NULL;
      }
    }

    if (TL_TD)
    {
      if (CheckTimer(N_ND->NodeTimer,TL_TD))
      {
        TL_TD=NULL;
        retval=IN_TIMEOUT; // just timeout. eventually everything will timeout and you'll get back
                           // to the command prompt whil will log you off..
      }
      else
      {
        AbortTimer(N_ND->NodeTimer,TL_TD);
        TL_TD=NULL;
      }

    }

    if (Timeout && retval==IN_NOTHING)
    {
      if (TD)
      {
        if (CheckTimer(N_ND->NodeTimer,TD))
        {
          TD=NULL;
          retval=IN_TIMEOUT;

          if ((Flags & GL_DISPLAY) && !(Flags & GL_NORETURN))
          {
            if (Flags & GL_SYSOP)
            {
              PutConText("\r\n");
            }
            else
            {
              PutText("\r\n");
            }
          }
        }
        else
        {
          // a key has been pressed so restart timer for string entry.
          AbortTimer(N_ND->NodeTimer,TD);
          TD=NULL;
        }
      }
    }

    if (N_ND->RequestShutdown) retval=IN_SHUTDOWN;
    if (N_ND->LoginType==LOGIN_REMOTE && CarrierLost()) retval=IN_LOSSCARRIER;

    while (retval==IN_NOTHING && HandleConSerSigs(ReturnedSigs)) // swapped!
    {
      // key press, restart timer!
      if (Timeout)
      {
        if (TD) AbortTimer(N_ND->NodeTimer,TD);
        TD=SubmitTimer(N_ND->NodeTimer,Timeout,0);
      }
      // ok, we got some data from somehwere...

      CurrentChar[0]=N_ND->IBuffer[0];

      // checkcsi() needs to know EXACTLY where to look for incoming data

      csitype=CheckCSI(CurrentChar[0],ReturnedSigs & N_ND->ConSig ? GET_CONSOLE : GET_SERIAL);
      switch (csitype)
      {
        case KEY_CSI:
          {
            if (Flags & GL_EDIT)
            {
              if (strcmp(N_ND->csistring,str_CSI_CURSORLEFT)==0)
              {
                if (xpos>0)
                {
                  xpos--;
                  if (Flags & GL_DISPLAY)
                  {
                    if (Flags & GL_SYSOP)
                    {
                      PutConText("\033[1D");
                    }
                    else
                    {
                      PutText("\033[1D");
//                      CursorLeft(1);
                    }
                  }
                }
              }
              else
              {
                if (strcmp(N_ND->csistring,str_CSI_CURSORRIGHT)==0)
                {
                  if (xpos<strlen(N_ND->CurrentLine))
                  {
                    xpos++;
                    if (Flags & GL_DISPLAY)
                    {
                      if (Flags & GL_SYSOP)
                      {
                        PutConText("\033[1C");
                      }
                      else
                      {
                        PutText("\033[1C");
  //                      CursorRight(1);
                      }
                    }
                  }
                }
                else
                {
                  if ((Flags & GL_HISTORY) && (HistoryItems>0))
                  {
                    if (strcmp(N_ND->csistring,str_CSI_CURSORUP)==0)
                    {
                      if (HistoryNum>0)
                      {
                        HistoryNum--;
                      }
                    }

                    if (strcmp(N_ND->csistring,str_CSI_CURSORDOWN)==0)
                    {
                      if (HistoryNum<HistoryItems)
                      {
                        HistoryNum++;
                      }
                    }

                    if (HistoryNum!=OldHistNum)
                    {
                      if (HistoryNum<HistoryItems)
                      {
                        strNcpy(N_ND->CurrentLine,HBBS_ListName(HistoryList,HistoryNum),MaxLen);
                        OldHistNum=HistoryNum;
                      }
                      else
                      {
                        N_ND->CurrentLine[0]=0; // erase the line.. :-)
                        HistoryNum=HistoryItems;
                        OldHistNum=-1;
                      }
                      ReDrawLine(Flags,&xpos);
                    }
                  }
                }
              }
            }
            retval=CheckFunctionKeys();
          }
          break;
        case KEY_ESCAPE:
          handleescape();
          // CheckCSI will leave one the character following the escape key
          // in the buffer. so reset out mini string again..
          CurrentChar[0]=N_ND->IBuffer[0];
          // no break so we process the next char as well..
        case KEY_NONE:
          if (N_ND->LoginType!=LOGIN_NONE || ReturnedSigs & N_ND->SerSig)
          {
            if (CurrentChar[0]>=0x20) // i.e. not unprintable or a control char..
            {
              if (CurrentChar[0]==127) // DEL
              {
                if (Flags & GL_EDIT)
                {
                  editlen=strlen(N_ND->CurrentLine);
                  if (xpos!=editlen)
                  {
                    for (editloop=xpos;editloop<((editlen<MaxLen ? editlen:MaxLen));editloop++) // <= to copy the null terminator as well!
                    {
                      N_ND->CurrentLine[editloop]=N_ND->CurrentLine[editloop+1];
                    }
                    if (Flags & GL_DISPLAY)
                    {
                      if (Flags & GL_SYSOP)
                      {
                        PutConText("\033[1P");
                      }
                      else
                      {
                        PutText("\033[1P");
                        if (Flags & GL_NODISTURB)
                        {
                          if (xpos==MaxLen-1)
                          {
                            strcpy(cmovestr,"\033[@");
                          }
                          else
                          {
                            sprintf(cmovestr,"\033[%dC\033[@\033[%dD",MaxLen-xpos-1,MaxLen-xpos-1);
                          }
                          PutText(cmovestr);
                        }
                      }
                    }
                  }
                }
              }
              else
              {
                if (Flags & GL_CVTUPPER) CurrentChar[0]=toupper(CurrentChar[0]);

                AddOK=FALSE;
                if (Flags & GL_USECHARS)
                {
                  if (strchr(N_ND->CharsAllowed,CurrentChar[0]))
                  {
                    AddOK=TRUE;
                  }
                  else
                  {
                    // ooh, wrong char, lets do a beep..
                    if (!(Flags & GL_NOBEEP))
                    {
                      // but only if we're allowed to!
                      if (Flags & GL_SYSOP)
                      {
                        PutConChar(7); // 7 = BEEP char!
                      }
                      else
                      {
                        PutChar(7);
                      }
                    }
                  }
                } else AddOK=TRUE;
                if (AddOK)
                {
                  // ok, no function key pressed, so add the char to the end of the string...
                  if ((editlen=strlen(N_ND->CurrentLine)) < (LEN_CURRENTLINE < MaxLen ? LEN_CURRENTLINE : MaxLen))
                  {
                    if (xpos==editlen) // tag char on end ?
                    {
                      xpos++;
                      strcat(N_ND->CurrentLine,CurrentChar);
                      if (Flags & GL_DISPLAY)
                      {
                        if (Flags & GL_SYSOP)
                        {
                          if (PasswordChar) PutConChar(PasswordChar); else PutConChar(CurrentChar[0]);
                        }
                        else
                        {
                          if (PasswordChar) PutChar(PasswordChar); else PutChar(CurrentChar[0]);
                        }
                      }
                    }
                    else
                    {
                      if (Flags & GL_EDIT)
                      {
                        // gotta insert char!
                        for (editloop=editlen+1; editloop>xpos ;editloop--) //+1 for null terminator
                        {
                          N_ND->CurrentLine[editloop]=N_ND->CurrentLine[editloop-1];
                        }
                        N_ND->CurrentLine[xpos]=CurrentChar[0];
                        if (Flags & GL_DISPLAY)
                        {
                          if (Flags & GL_SYSOP)
                          {
                            PutConText("\033[1@");
                            PutConChar(CurrentChar[0]);
                          }
                          else
                          {
                            PutText("\033[1@");
                            PutChar(CurrentChar[0]);
                            if (Flags & GL_NODISTURB)
                            {
                              sprintf(cmovestr,"\033[%dC\033[P\033[%dD",MaxLen-xpos-1,MaxLen-xpos-1);
                              PutText(cmovestr);
                            }
                          }
                        }
                        xpos++;
                      }
                    }
                  }
                }
              }
            }
            else
            {
              switch (CurrentChar[0])
              {
                case 13: // return
                  if ((Flags & GL_DISPLAY) && !(Flags & GL_NORETURN)) PutText(str_CRLF); // linefeed...
                  retval=IN_GOTLINE;
                  break;

                case 27: //escape
                  handleescape();
                  break;

                case 3: //ctrl+c
                  if (!(N_ND->NodeFlags & NFLG_CTRLC)) N_ND->NodeFlags+=NFLG_CTRLC;
                  break;
                case 24: //ctrl+x, clear the line..
                  N_ND->CurrentLine[0]=0;
                  ReDrawLine(Flags,&xpos);
                  break;

                case 8: //backspace
                  if (Flags & GL_EDIT)
                  {
                    if (xpos>0)
                    {
                      xpos--;
                      editlen=strlen(N_ND->CurrentLine);
                      for (editloop=xpos;editloop<editlen;editloop++) // <= to copy the null terminator as well!
                      {
                        N_ND->CurrentLine[editloop]=N_ND->CurrentLine[editloop+1];
                      }
                      if (Flags & GL_DISPLAY)
                      {
                        if (Flags & GL_SYSOP)
                        {
                          PutConText("\033[1D\033[1P");
                        }
                        else
                        {
                          if (Flags & GL_NODISTURB)
                          {
                            if (xpos==MaxLen-1)
                            {
                              strcpy(cmovestr,"\033[@");
                            }
                            else
                            {
                              sprintf(cmovestr,"\033[%dC\033[@\033[%dD",MaxLen-xpos-1,MaxLen-xpos-1);
                            }
                            PutText(cmovestr);
                          }
                          PutText("\033[1D\033[1P");
//                          CursorLeft(1);
//                          DeleteChars(1);
                        }
                      }
                    }
                  }
                  break;
              }
            }
          }
          break;
      }
    }
    if (retval==IN_NOTHING)
    {
      if ((strlen(N_ND->CurrentLine)==MaxLen) && (Flags & GL_IMMEDIATE))
      {
        retval=IN_GOTLINE;

        if (Flags & GL_LINEWRAP)
        {
          if (strptr=strrchr(N_ND->CurrentLine,' '))
          {
            if (Flags & GL_DISPLAY)
            {
              sprintf(cmovestr,"\033[%dD\033[K",strlen(N_ND->CurrentLine)-(strptr-N_ND->CurrentLine));
              if (Flags & GL_SYSOP)
              {
                PutConText(cmovestr);
              }
              else
              {
                PutText(cmovestr);
              }
            }

            strcpy(N_ND->CurrentLineWrap,strptr+1);
            *strptr=0;

            // *C* update the display!
          }
        }


        if ((Flags & GL_DISPLAY) && !(Flags & GL_NORETURN)) PutText(str_CRLF); // linefeed...
      }
    }
  }

  AbortTimer(N_ND->NodeTimer,TD);
  AbortTimer(N_ND->NodeTimer,TO_TD);
  AbortTimer(N_ND->NodeTimer,TL_TD);

  if (Flags & GL_HISTORY)
  {
    if (N_ND->CurrentLine[0])
    {
      if (!NewStrNode(N_ND->CurrentLine,HistoryList))
      {
        HistoryItems++;
      }
    }
  }

  if (retval==IN_LOSSCARRIER)
  {
    N_ND->OnlineStatus=OS_OFFLINE;
    if (Flags & GL_DISPLAY) PutConText(str_CRLF);
  }
  return(retval);
}

void RunDoor(char *doorname,char *options,BOOL DebugMode,BOOL ASync)
{
  // function to start a door
  // *C* have timeout on door, check file exists before attempting to run it...

  char cmd[BIG_STR],outstr[BIG_STR+300];
//  sprintf(cmd,"%s %d %s",doorname,N_NodeNum,options ? options : "\0");
  sprintf(outstr,"%d",N_NodeNum);
  replace(cmd,doorname,"{O}",options ? options : ""); //replace won't work with a null string
  replace(cmd,cmd,"{N}",outstr);

  // if doorlog is on then write to the doorlog file!
  if (N_ND->NodeSettings.DoorLog || N_ND->DoorLogOverride)
  {
    HBBS_GetDate(outstr);
    strcat(outstr," ");
    HBBS_GetTime(outstr+strlen(outstr));
    sprintf(outstr+strlen(outstr)," Handle: %s, Conf: %s, Door: %s\n",N_ND->User.CallData.Handle,N_ND->CurrentConf ? N_ND->CurrentConf->node.ln_Name : "None Joined",cmd);
    HBBS_AppendStrToFile(N_ND->NodeSettings.DoorLogFile,outstr);
  }

  if (DebugMode)
  {

    printf("DOOR DEBUG MODE ON - Start the door with the settings below!\n"
           "==============================================================================\n"
           "%s\n"
           "==============================================================================\n",cmd);
  }
  else
  {
    HBBS_RunDOSCMD(cmd,ASync);
  }
}

struct MsgPort* CreateDoorStartPort( void )
{
  struct MsgPort *DP=NULL;
  sprintf(N_ND->DoorStartPortName,"DoorStart_N%d_D%d",N_ND->NodeNum,N_ND->DoorsRunning+1);
  DP=CreatePort(N_ND->DoorStartPortName,0);
  return(DP);
}

BOOL CheckDoorStarted(struct MsgPort *DP,char *systemoptions)
{
  struct DoorActivityMsg *DM;
  BOOL retval=FALSE;

  while (DM=(struct DoorActivityMsg *)GetMsg(DP))
  {
    N_ND->ActiveDoor->SystemOptions=systemoptions;
    ReplyMsg((struct Message *)DM);
    retval=TRUE;
  }
  return(retval);
}

void GoDoor(char *doorname,char *systemoptions,char *options,BOOL DebugMode)
{
  ULONG ReturnedSigs;
  struct DoorData *CurrentDoor;
  struct TimerData *TD;
  BOOL timeout,doorstarted;
  struct MsgPort *DP;
  char *clinesave=N_ND->CurrentLine; // save old line..

// *C* Why have I commented this out ?  is it because of the {N} {O}'s etc... i think it might be!
//  if (PathOK(doorname)) // does door exist ?
//  {
//    strcpy(N_ND->Action,"Loading Door!");
//    DOOR_UpdateNodeStatus(UPD_ACTION);

    if (N_ND->CurrentLine=AllocVec(LEN_CURRENTLINE,MEMF_PUBLIC))
    {
      N_ND->CurrentLine[0]=0;

      if (TD=SubmitTimer(N_ND->NodeTimer,BBSGlobal->Door_Timeout,0))
      {
        timeout=FALSE;
        doorstarted=FALSE;

        if (DP=CreateDoorStartPort())
        {
          N_ND->ActiveDoor=NULL;
          RunDoor(doorname,options,DebugMode,TRUE);

//        strcpy(N_ND->Action,"Waiting For Door");
//        DOOR_UpdateNodeStatus(UPD_ACTION);

          do
          {
            Wait (1L<<DP->mp_SigBit | DEF_PORTSIG | DEF_TIMERSIG);
            doorstarted=CheckDoorStarted(DP,systemoptions);
            if (timeout=CheckTimer(N_ND->NodeTimer,TD))
            {
              TD=NULL;
            }
          } while(doorstarted==FALSE && timeout==FALSE);

          DeletePort(DP);

          if (timeout)
          {
            HBBS_LogError(N_ND->NodeSettings.NodeLogFile,ERR_DOORTIMEOUT,doorname,TYPE_WARNING);
          }
          else
          {
            if (TD) AbortTimer(N_ND->NodeTimer,TD);

            strNcpy(N_ND->Action,N_ND->ActiveDoor->node.ln_Name,MAX_ACTION_LEN);
            DOOR_UpdateNodeStatus(UPD_ACTION);

            if (N_ND->ActiveDoor!=NULL)
            {
              // This next loop is essentially the main loop of the bbs when
              // a door is running! :-)

              // wait until the door stops!

              CurrentDoor=N_ND->ActiveDoor;
              do
              {
                SetUpDoorSigs();
                ReturnedSigs=Wait(N_ND->ConWinSig |
                                  N_ND->SettingsWinSig |
                                  N_ND->InfoWinSig |
                                  N_ND->WinSig |
                                  N_ND->PortSig);

                HandleMiscSigs(ReturnedSigs);

                // while the door is started we should check for function key presses
                // and so on... (which is handled by HandleDoorMsg())


                // repeat wait loop until the door is finished, can detect by looking to see
                // what the value of the door pointer is when it starts, and then keep comparing
                // that to the the ActiveDoor pointer, if they differ the door has closed (as
                // the ActiveDoor Pointer would point to a different door or to NULL...
              } while (N_ND->ActiveDoor==CurrentDoor);

              if (DebugMode)
              {
                printf("Door return: ");
                puts(N_ND->DoorReturn);
              }
            }
          }
        }
        else
        {
          if (TD) AbortTimer(N_ND->NodeTimer,TD);
        }
      }
      FreeVec(N_ND->CurrentLine);
      N_ND->CurrentLine=clinesave;
    }
/* *C* Why have I commented this out ? .. because of the {x}'s you plonka!
  }
  else
  {
    sprintf(outstr,"The door file (%s) is missing!\r\n",doorname);
    PutText(outstr);
    HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_GENERAL,outstr,TYPE_WARNING);
  }
*/
  if (N_ND->ActiveDoor)
  {
    strNcpy(N_ND->Action,N_ND->ActiveDoor->node.ln_Name,MAX_ACTION_LEN);
    DOOR_UpdateNodeStatus(UPD_ACTION);
  }

  // check the carrier status after each door.
  if (CarrierLost())
  {
    N_ND->OnlineStatus=OS_OFFLINE;
  }
}

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

void CheckDoorTypes(char *actualtype,char *cmd,char *options)
{
  char tmpstr[BIG_STR],numstr[5];

  sprintf(numstr,"%d",N_NodeNum);
  replace(tmpstr,cmd,"{N}",numstr);
  replace(tmpstr,tmpstr,"{O}",options);

  if (stricmp(actualtype,"DOS")==0)
  {
    HBBS_RunDOSCMD(tmpstr,FALSE);
  }
  else
  {
    if (N_ND->OnlineStatus==OS_ONLINE || N_ND->RunningAwait)
    {
      if (stricmp(actualtype,"SSCREEN")==0)
      {
        DisplaySpecialScreen(tmpstr);
      }
      else
      {
        if (stricmp(actualtype,"CLS")==0)
        {
          PutText(ANSI_CLS);
        }
        else
        {
          if (stricmp(actualtype,"PAUSE")==0)
          {
            PausePrompt(tmpstr,0);
          }
          else
          {
            if (stricmp(actualtype,"ECHO")==0)
            {
              PutText(tmpstr);
              PutText("\r\n");
            }
            else
            {
              if (stricmp(actualtype,"SCREEN")==0)
              {
                DisplayScreen(tmpstr);
              }
            }
          }
        }
      }
    } /* online check */
  }
}

#define GOSYS_CONF 1
#define GOSYS_NODE 2
#define GOSYS_CMDS 3

V_BOOL GoSystemDoor( char *doorname, char *options )
{
  /* must search the SYSTEM door list in the commands dir for first the conf,
  then the node and finally the hbbs:commands directories.. when it finds
  it it runs it, note that there may be more than one door to call at a time
  as you may define more than one door for each system function. e.g.

  Examine_Type_1=HBBS
  Examine_DOOR_1=HBBS:Doors/User/Examine/Examine.HBBS
  Examine_Param_1=

  Examine_Type_2=HBBS
  Examine_DOOR_2=HBBS:Doors/User/DIZTimeAdd/DIZTimeAdd.HBBS
  Examine_Param_2=[=- Uploaded At HH:MM -=]

  Examine_Type_3=HBBS
  Examine_DOOR_3=HBBS:Doors/User/Sentby/Sentby.HBBS
  Examine_Param_3=Sent,Cps,Node

  note: if you have say Examine_Type_1 in hbbs:conferences/newuser/commands/system
  and in hbbs:commands/system the set of commands in hbbs:commands/system WILL NOT
  be run as the former file has priority over the latter.

  */

  UBYTE filename[BIG_STR],*optionname,*actualparam=NULL,*actualtype=NULL;
  V_BOOL done=FALSE,DebugMode;
  short whichfile;
  struct CfgFileData *CfgFile;
  struct List *CmdList=NULL;
  struct Node *node;
  short doornum;

  N_ND->DoorReturn[0]=0; // null terminate string...
  N_ND->DoorContinue=TRUE;

  for (whichfile=GOSYS_CONF;whichfile<=GOSYS_CMDS && !done;whichfile++)
  {


//    if (stricmp("AWAIT",doorname)!=0 && stricmp("ACCOUNTEDIT",doorname)!=0 && N_ND->OnlineStatus==OS_OFFLINE)
//    {
//      done=TRUE;
//    }
//    else
//    {
    filename[0]=0;
    switch(whichfile)
    {
      case GOSYS_CONF:
        if (N_ND->CurrentConf) // have we joined a conf yet ?
        {
          strcpy(filename,N_ND->CurrentConf->ConfPath);
          strcat(filename,FILENAME_CMDSSYSTEM);
        }
        break;
      case GOSYS_NODE:
        strcpy(filename,N_ND->NodeLocation);
        strcat(filename,FILENAME_CMDSSYSTEM);
        break;
      case GOSYS_CMDS:
        strcpy(filename,FILE_HBBS);
        strcat(filename,FILENAME_CMDSSYSTEM);
        break;
    }
    if (filename[0]) // string present ?
    {
      if (optionname=AllocVec(strlen(doorname)+10,MEMF_PUBLIC))
      {
        strcpy(optionname,doorname);
        strcat(optionname,"#?");
        if (CfgFile=HBBS_LoadConfigQuick(filename,LCFG_NONE,optionname))
        {
          strcpy(optionname,doorname);
          strcat(optionname,"_Door");

          if (HBBS_GetSetting(CfgFile,(void *)&CmdList,VTYPE_STRINGLIST,optionname,OPT_MULTI))
          {
            for (doornum=1,node = CmdList->lh_Head ; N_ND->DoorContinue && node->ln_Succ ; node =node->ln_Succ,doornum++)
            {
              actualtype=NULL;
              sprintf(optionname,"%s_Type_%d",doorname,doornum);
              if (HBBS_GetSetting(CfgFile,(void *)&actualtype,VTYPE_STRING,optionname,OPT_SINGLE))
              {
                actualparam=NULL;
                sprintf(optionname,"%s_Param_%d",doorname,doornum);
                HBBS_GetSetting(CfgFile,(void *)&actualparam,VTYPE_STRING,optionname,OPT_SINGLE);
                if (!actualparam) actualparam=DupStr("");
                if (actualparam)
                {
                  done=TRUE;

                  DebugMode=FALSE;

                  sprintf(optionname,"%s_Debug_%d",doorname,doornum);
                  HBBS_GetSetting(CfgFile,(void *)&DebugMode,VTYPE_BOOL,optionname,OPT_SINGLE);

                  if (stricmp(actualtype,"HBBS")==0) //changed from "NORMAL" on 1-JULY-96
                  {
                    GoDoor(node->ln_Name,actualparam,options,DebugMode);
                  }
                  else
                  {
                    if (stricmp(actualtype,"CLI")==0)
                    {
                      RunDoor(node->ln_Name,options,DebugMode,FALSE);
                    }
                    else
                    {
                      CheckDoorTypes(actualtype,node->ln_Name,options);
                    }
                  }
                  FreeStr(actualparam);
                }
                FreeStr(actualtype);
              }
            }
            FreeStrList(CmdList);
          }
          HBBS_FlushConfig(CfgFile);
        }
        FreeVec(optionname);
      }
    }
//    }
  }
  return(done);
}

short WhatAccessNode(V_SMALLNUM accesslevel)
{
  // returns the struct node number of the specified access level
  // mainly used when you want to find the name of an access level

  short retval=-1;
  char tmpstr[10];
  short loop;
  struct Node *node;


  sprintf(tmpstr,"%d",accesslevel);

  for (loop=0,node = BBSGlobal->AcsLevelList->lh_Head ; node->ln_Succ && retval==-1 ; node =node->ln_Succ,loop++)
  {
    if (strcmp(tmpstr,node->ln_Name)==0)
    {
      retval=loop;
    }
  }
  return(retval);
}

#define GOUSER_CONF 1
#define GOUSER_NODE 2
#define GOUSER_CMDS 3


V_BOOL GoUserDoor( char *doorname, char *options )
{

  /* this door must first determine the current access level, then scan the following
     files to find out what door to run.

     CurrentConf/Commands/Level_XX
     CurrentConf/Commands/AllLevels
     Currentnode/Commands/Level_XX
     Currentnode/Commands/AllLevels
     HBBS:Commnds/Level_XX
     HBBS:Commnds/AllLevels

     then when it finds the door to run it calls it up!

  */

  UBYTE dirname[BIG_STR],filename[BIG_STR],*optionname,*actualparam=NULL,*actualtype=NULL;
  V_BOOL done=FALSE,DebugMode,SearchedAll;
  short whichfile;
  struct CfgFileData *CfgFile;
  struct List *CmdList=NULL;
  struct Node *node;
  short doornum,CurrentAccess;

  N_ND->DoorReturn[0]=0; // null terminate string...
  N_ND->DoorContinue=TRUE;

  for (whichfile=GOUSER_CONF;whichfile<=GOUSER_CMDS && !done;whichfile++)
  {
    dirname[0]=0;
    filename[0]=0;
    switch(whichfile)
    {
      case GOUSER_CONF:
        if (N_ND->CurrentConf) // have we joined a conf yet ?
        {
          sprintf(dirname,"%sCommands/",N_ND->CurrentConf->ConfPath);
        }
        break;
      case GOUSER_NODE:
        sprintf(dirname,"%sCommands/",N_ND->NodeLocation);
        break;
      case GOUSER_CMDS:
        strcpy(dirname,"HBBS:Commands/");
        break;
    }
    if (dirname[0]) // string present ? cos it might not be if we ain't in a conf..
    {
      // ok, no we know where to look for the commands, so we gotta check
      // "Level_<access>" downto "Level_00" until we find it, if it ain't there then
      // we have to check "All_Levels"

      SearchedAll=FALSE;
      CurrentAccess=WhatAccessNode(N_ND->User.CallData.Access);

      do
      {
        if (CurrentAccess<0)
        {
          sprintf(filename,"%sAll_Levels",dirname);
          SearchedAll=TRUE;
        }
        else
        {
          node=GetNode(BBSGlobal->AcsLevelList,CurrentAccess);

          sprintf(filename,"%sLevel_%s",dirname,node->ln_Name);
        }

        if (optionname=AllocVec(strlen(doorname)+10,MEMF_PUBLIC))
        {
          strcpy(optionname,doorname);
          strcat(optionname,"#?");
          if (CfgFile=HBBS_LoadConfigQuick(filename,LCFG_NONE,optionname))
          {
            strcpy(optionname,doorname);
            strcat(optionname,"_Door");
            if (HBBS_GetSetting(CfgFile,(void *)&CmdList,VTYPE_STRINGLIST,optionname,OPT_MULTI))
            {
              for (doornum=1,node = CmdList->lh_Head ; N_ND->DoorContinue && node->ln_Succ ; node =node->ln_Succ,doornum++)
              {
                actualtype=NULL;
                sprintf(optionname,"%s_Type_%d",doorname,doornum);
                if (HBBS_GetSetting(CfgFile,(void *)&actualtype,VTYPE_STRING,optionname,OPT_SINGLE))
                {
                  actualparam=NULL;
                  sprintf(optionname,"%s_Param_%d",doorname,doornum);
                  HBBS_GetSetting(CfgFile,(void *)&actualparam,VTYPE_STRING,optionname,OPT_SINGLE);
                  if (!actualparam) actualparam=DupStr("");
                  if (actualparam)
                  {
                    DebugMode=FALSE;
                    if (stricmp(actualtype,"HBBS")==0)
                    {
                      sprintf(optionname,"%s_Debug_%d",doorname,doornum);
                      HBBS_GetSetting(CfgFile,(void *)&DebugMode,VTYPE_BOOL,optionname,OPT_SINGLE);

                      GoDoor(node->ln_Name,actualparam,options,DebugMode);
                    }
                    else
                    {
                      if (stricmp(actualtype,"CLI")==0)
                      {
                        RunDoor(node->ln_Name,options,DebugMode,FALSE);
                      }
                      else
                      {
                        CheckDoorTypes(actualtype,node->ln_Name,options);
                      }
                    }
                    FreeStr(actualparam);
                  }
                  FreeStr(actualtype);
                }
              }
              FreeStrList(CmdList);
              done=TRUE;
            }
            HBBS_FlushConfig(CfgFile);
          }
          FreeVec(optionname);
        }
        CurrentAccess--;

      } while (!( done || SearchedAll));

    }
  }
  return(done);
}

// *M* Move routines into HBBSNode.C

void Add_Last_Caller(char *data)
{
  struct Node *node;

  if (N_ND->Current_Last_Callers==N_ND->Max_Last_Callers)  // *C* make configurable
  {
    node=RemTail(N_ND->Last_Callers);
    FreeStr(node->ln_Name);
    FreeVec(node);
    N_ND->Current_Last_Callers--;
  }
  if (node=(struct Node*)AllocVec(sizeof(struct Node),MEMF_PUBLIC|MEMF_CLEAR))
  {
    node->ln_Name=DupStr(data);
    AddHead(N_ND->Last_Callers,node);
    N_ND->Current_Last_Callers++;
  }
  UpdateInfoWin();
  SendRequest(REQ_UPDATEINFO);
}

void Add_Last_PWFail(char *data)
{
  struct Node *node;

  if (N_ND->Current_Last_PWFails==N_ND->Max_Last_PWFails)
  {
    node=RemTail(N_ND->Last_PWFails);
    FreeStr(node->ln_Name);
    FreeVec(node);
    N_ND->Current_Last_PWFails--;
  }
  if (node=(struct Node*)AllocVec(sizeof(struct Node),MEMF_PUBLIC|MEMF_CLEAR))
  {
    node->ln_Name=DupStr(data);
    AddHead(N_ND->Last_PWFails,node);
    N_ND->Current_Last_PWFails++;
  }
  UpdateInfoWin();
  SendRequest(REQ_UPDATEINFO);
}

void Add_Last_Download(char *data)
{
  struct Node *node;

  if (N_ND->Current_Last_Downloads==N_ND->Max_Last_Downloads)
  {
    node=RemTail(N_ND->Last_Downloads);
    FreeStr(node->ln_Name);
    FreeVec(node);
    N_ND->Current_Last_Downloads--;
  }
  if (node=(struct Node*)AllocVec(sizeof(struct Node),MEMF_PUBLIC|MEMF_CLEAR))
  {
    node->ln_Name=DupStr(data);
    AddHead(N_ND->Last_Downloads,node);
    N_ND->Current_Last_Downloads++;
  }
  UpdateInfoWin();
  SendRequest(REQ_UPDATEINFO);
}

void Add_Last_Upload(char *data)
{
  struct Node *node;

  if (N_ND->Current_Last_Uploads==N_ND->Max_Last_Uploads)
  {
    node=RemTail(N_ND->Last_Uploads);
    FreeStr(node->ln_Name);
    FreeVec(node);
    N_ND->Current_Last_Uploads--;
  }
  if (node=(struct Node*)AllocVec(sizeof(struct Node),MEMF_PUBLIC|MEMF_CLEAR))
  {
    node->ln_Name=DupStr(data);
    AddHead(N_ND->Last_Uploads,node);
    N_ND->Current_Last_Uploads++;
  }
  UpdateInfoWin();
  SendRequest(REQ_UPDATEINFO);
}


#define DSS_CONF 1
#define DSS_NODE 2
#define DSS_HBBS 3
#define DSS_ERR  4

V_BOOL DisplaySpecialScreen(char *ScreenName)
{
  char filename[BIG_STR],filename2[BIG_STR];
  BOOL TriedAllLevels,Done=FALSE;
  struct Node *node;

  short whichdir=DSS_CONF;
  short CurrentAccess;

  do
  {
    if (N_ND->User.Valid)
    {
      TriedAllLevels=FALSE;
      CurrentAccess=WhatAccessNode(N_ND->User.CallData.Access);
    }
    else
    {
      CurrentAccess=-1;
    }

    do
    {
      filename[0]=0;

      switch(whichdir)
      {
        case DSS_CONF:
          if (N_ND->CurrentConf)
          {
            strcpy(filename,N_ND->CurrentConf->ConfPath);
          }
          break;
        case DSS_NODE:
          strcpy(filename,N_ND->NodeLocation);

          // if we are to check for screens in <nodepath>/screens/special then break the switch()
          if (N_ND->NodeSettings.CheckNodeScreens) break;

          whichdir=DSS_HBBS;
          // and follow on..
        case DSS_HBBS:
          strcpy(filename,"HBBS:");
          break;
      }

      if (filename[0])
      {
        if (CurrentAccess<0) TriedAllLevels=TRUE;

        strcat(filename,DIRNAME_SCREENSSPECIAL);
        strcat(filename,ScreenName);

        if (N_ND->User.Valid && !TriedAllLevels)
        {
          // add access level _XX here..

          node=GetNode(BBSGlobal->AcsLevelList,CurrentAccess);
          CurrentAccess--;
          strcat(filename,"_");
          strcat(filename,node->ln_Name);
        }

        // copy the filename (without the language extension on, to another varaible)
        // so that we can try a default if a language extn file does not exist.

        strcpy(filename2,filename);

        // Add the language extention to the filename

        if (N_ND->User.Valid)
        {
          node=GetNode(BBSGlobal->LanguageExtn,N_ND->User.CallData.Language-1); //offset starts at 1
        }
        else
        {
          node=BBSGlobal->LanguageExtn->lh_Head;
        }
        strcat(filename,node->ln_Name);  // Change To Users Selected lanuage type


        // attempt to display the screen

        Done=DisplayScreen(filename);

        // if the screen file didn't exist and there's more than one language, search for
        // a default screen

        if ( (!Done) && (BBSGlobal->Languages > 1) )
        {
          // now try to display a screen using the same path and access level, but using
          // _DEF.TXT instead of the language.

          strcat(filename2,"_DEF.TXT");

          // attempt to display the screen again

          Done=DisplayScreen(filename2);
        }

      } else TriedAllLevels=TRUE;

    } while (!TriedAllLevels && !Done);


    whichdir++;

  } while (! ((Done) || (whichdir==DSS_ERR)) );
  return(Done);
}

void SaveFileTags( void )
{
  struct CfgFileData *cfgfile;
  char tmpstr[BIG_STR];
  char optionstr[20];
  struct TaggedFile *filetagnode;
  int loop=0;

  sprintf(tmpstr,"HBBS:System/Data/Users/%d/TaggedFiles.CFG",N_ND->User.CallData.UserID);

  if (cfgfile=HBBS_CreateConfig(tmpstr))
  {
    // this item is ignored, it's just for the sysop's benefit so he can look at a file and see whose tags are in it..

    HBBS_AddCfgItem(cfgfile,"UserName",N_ND->User.CallData.Handle);


    for (filetagnode = (struct TaggedFile *)N_ND->TaggedFileList->lh_Head ; filetagnode->node.ln_Succ ; filetagnode =(struct TaggedFile*)filetagnode->node.ln_Succ)
    {
      loop++;


      if (filetagnode=(struct TaggedFile *)GetNode(N_ND->TaggedFileList,loop-1)) // get the node
      {

        sprintf(optionstr,"FileName_%d",loop);
        HBBS_AddCfgItem(cfgfile,optionstr,filetagnode->node.ln_Name);

        sprintf(optionstr,"WarezFile_%d",loop);
        HBBS_AddCfgItem(cfgfile,optionstr,filetagnode->WarezFile ? "YES" : "NO");

        sprintf(optionstr,"ConferenceNum_%d",loop);
        sprintf(tmpstr,"%d",filetagnode->ConferenceNum);
        HBBS_AddCfgItem(cfgfile,optionstr,tmpstr);

        sprintf(optionstr,"FileSize_%d",loop);
        sprintf(tmpstr,"%1.0f",filetagnode->FileSize);
        HBBS_AddCfgItem(cfgfile,optionstr,tmpstr);
      }
    }
    HBBS_SaveConfig(cfgfile);
    HBBS_FlushConfig(cfgfile);
  }
}

void LoadFileTags( void )
{
  struct CfgFileData *cfgfile;
  char tmpstr[BIG_STR];
  char optionstr[20];
  char *strptr;
  struct TaggedFile *filetagnode;
  BOOL Error=FALSE;
  N_ND->TaggedFiles=0;

  // reinitialise list..
  NewList(N_ND->TaggedFileList);

  sprintf(tmpstr,"HBBS:System/Data/Users/%d/TaggedFiles.CFG",N_ND->User.CallData.UserID);

  if (cfgfile=HBBS_LoadConfig(tmpstr,LCFG_NONE))
  {
    do
    {
      Error=TRUE;
      strptr=NULL;

      sprintf(optionstr,"FileName_%d",N_ND->TaggedFiles+1);
      if (HBBS_GetSetting(cfgfile,(void *)&strptr,VTYPE_STRING,optionstr,OPT_SINGLE))
      {
        if (filetagnode=AllocVec(sizeof(struct TaggedFile),MEMF_PUBLIC))
        {
          N_ND->TaggedFiles++;
          filetagnode->node.ln_Name=strptr;


          sprintf(optionstr,"WarezFile_%d",N_ND->TaggedFiles);
          if (HBBS_GetSetting(cfgfile,(void *)&filetagnode->WarezFile,VTYPE_BOOL,optionstr,OPT_SINGLE))
          {

            sprintf(optionstr,"ConferenceNum_%d",N_ND->TaggedFiles);
            if (HBBS_GetSetting(cfgfile,(void *)&filetagnode->ConferenceNum,VTYPE_BIGNUM,optionstr,OPT_SINGLE))
            {

              // *C* double check file sizes of tagged files
              //

              if (PathOK(filetagnode->node.ln_Name))
              {

                if((filetagnode->FileSize=(float)HBBS_GetFileSize(filetagnode->node.ln_Name)) > 0)
                {
                  Error=FALSE;
                  AddTail(N_ND->TaggedFileList,(struct Node*) filetagnode);
                }
              }
              // else display error message to user that file could not be tagged ?

// we used to use this bit, but this might prove to be unreliable because of possible
// incorrect file sizes being set, so now we check them
/*
              sprintf(optionstr,"FileSize_%d",N_ND->TaggedFiles);
              if (HBBS_GetSetting(cfgfile,(void *)&filetagnode->FileSize,VTYPE_HUGENUM,optionstr,OPT_SINGLE))
              {
                Error=FALSE;
                AddTail(N_ND->TaggedFileList,(struct Node*) filetagnode);
              }
*/
            }
          }
          if (Error)
          {
            FreeStr(strptr);
            FreeVec(filetagnode);
            N_ND->TaggedFiles--;
          }
        }
      }
    } while (Error==FALSE);
    HBBS_FlushConfig(cfgfile);
  }
}


void ResetVars( void )
{
  LONG loop;
  char tmpfilename[BIG_STR];

  for (loop=0;loop<LEN_ACTIONS;loop++)
  {
    N_ND->Actions[loop]=ACTC_NONE;
  }
  N_ND->Actions[LEN_ACTIONS]=0;

  N_ND->NodeFlags=NFLG_NONE;
  N_ND->User.Valid=FALSE;
  N_ND->Action[0]=0;
  N_ND->DoorReturn[0]=0;
  N_ND->CurrentLine[0]=0;
  N_ND->CurrentLineWrap[0]=0;
  N_ND->CurrentConf=NULL;
  N_ND->MaxDIZLines=N_ND->NodeSettings.MaxDIZLines;
  N_ND->User.FilesUploaded=0L;
  N_ND->User.FilesDownloaded=0L;
  N_ND->User.BytesUploaded=0;
  N_ND->User.BytesDownloaded=0;
  N_ND->User.PagesMade=0L;
  N_ND->User.NukedFiles=0L;
  N_ND->User.MsgsRead=0L;
  N_ND->User.MsgsWritten=0L;
  N_ND->DoorLogOverride=FALSE;
  HBBS_SetBBSCols();
  HBBS_SetBBSStrings();

  // remove file
  strcpy(tmpfilename,N_ND->NodeLocation);
  strcat(tmpfilename,"Work/SkippedFiles.TXT");
  DeleteFile(tmpfilename);
}

void FreeData( void )
{
  if (N_ND->User.Valid)
  {
    N_ND->User.NormalData.TimeUsed+=HBBS_TimeOnline();
    HBBS_SaveUserData(&N_ND->User.NormalData);
    SaveFileTags();
    FreeFileTags(N_ND);
  }
  N_ND->OnlineStatus=OS_OFFLINE;
}

char *ftoa(V_HUGENUM num,char *str) // *C* move to HBBSCommon.library
{
  str[0]='0';
  str[1]=0;

  sprintf(str,"%0.0f",num);

  return(str);
}

void GoBBS( void )
{
  char tmpstr[1024],confstr[10],datestr[LEN_DATESTR+1],timestr[LEN_TIMESTR+1];
  V_BIGNUM confnum,loop,loop2;
  BOOL ReLogin=FALSE;

  do
  {
    ResetVars();
    PutText(str_CLS);
    PutText("\033[37;1m");

    sprintf(tmpstr,"HNode%dLoginType",N_ND->NodeNum);

    switch(N_ND->LoginType)
    {
      case LOGIN_LOCAL:
        ConWriteStr("-=LOCAL LOGIN=-\r\n");
        SetVar(tmpstr,"LOCAL",-1 ,GVF_GLOBAL_ONLY);
        break;
      case LOGIN_REMOTE:
        ConWriteStr("-=REMOTE LOGIN=-\r\n");
        SetVar(tmpstr,"REMOTE",-1 ,GVF_GLOBAL_ONLY);
        break;
    }

    sprintf(tmpstr,"***********[-CONNECTION ESTABLISHED-]***********[%s*", N_ND->LoginType==LOGIN_REMOTE ? "REMOTE]" : "LOCAL]*");
    HBBS_AddToCallersLog(tmpstr);


    PutText(ANSI_RESET);

    GoSystemDoor("FRONTEND",NULL);
    if (N_ND->OnlineStatus==OS_ONLINE)
    {
      // ok, check to see what the frontend door returned..
      strcpy(tmpstr,N_ND->DoorReturn);

      if (iposition("HACK",tmpstr)>=0)
      {
        sprintf(tmpstr,"Hack Attempt - %s",(N_ND->User.Valid ? N_ND->User.CallData.Handle : ""));
        HBBS_AddToCallersLog(tmpstr);

        // update list of PW Fails..

        if (N_ND->User.Valid)
        {
          Add_Last_PWFail(N_ND->User.CallData.Handle); // add to both...
          Add_Last_Caller(N_ND->User.CallData.Handle);
          N_ND->Actions[ACTN_HACK]=ACTC_HACK;
        }

      }
      else
      if (iposition("FAILED",tmpstr)>=0)
      {
        // do something.. (update calls log)

        sprintf(tmpstr,"Failed Login - %s",(N_ND->User.Valid ? N_ND->User.CallData.Handle : ""));
        HBBS_AddToCallersLog(tmpstr);
      }
      else
      if (iposition("LOGGEDIN",tmpstr)>=0)
      {
        N_ND->Actions[ACTN_CARRIERLOST]=ACTC_CARRIERLOST; // the LOGOUT door ('G') must set this to ACTC_NONE

        sprintf(tmpstr,"User logged in, Handle: %s Group: %s Speed: %s",N_ND->User.CallData.Handle,N_ND->User.CallData.Group,N_ND->ConnectBaud);
        HBBS_AddToCallersLog(tmpstr);

        HBBS_SetAccess();

        HBBS_SetWatchTitles();
        DOOR_UpdateNodeStatus(UPD_NAME);
        DOOR_UpdateNodeStatus(UPD_GROUP);
        Add_Last_Caller(N_ND->User.CallData.Handle);

        HBBS_GetDate(datestr);
        HBBS_GetTime(timestr);

        // update the users stats..

        HBBS_GetDateString(tmpstr,N_ND->User.CallData.LastCalledDate);
        if (stricmp(tmpstr,datestr)!=0) // date last user called is different from today..
        {
          N_ND->User.CallData.TimeUsed=0;
          N_ND->User.NormalData.TimeUsed=0;
        }

        N_ND->User.NormalData.CallsMade++;
        N_ND->User.CallData.CallsMade++;
        N_ND->User.CallData.LastCalledDate=HBBS_GetAmigaTime();
        N_ND->User.NormalData.LastCalledDate=N_ND->User.CallData.LastCalledDate;

        // user logged in ok.
        sprintf(tmpstr,"HNode%dUser",N_ND->NodeNum);
        SetVar(tmpstr,N_ND->User.CallData.Handle,-1,GVF_GLOBAL_ONLY);
        LoadFileTags();

        if (stricmp(BBSGlobal->LastCalledDate,datestr)==0)
        {
          BBSGlobal->CallsToday++;
          N_ND->CallsToday++;
        }
        else
        {
          BBSGlobal->CallsToday=1;
          N_ND->CallsToday=1;
        }

        BBSGlobal->CallsEver++;

        strcpy(BBSGlobal->LastCalledDate,datestr);
        strcpy(BBSGlobal->LastCalledTime,timestr);

        strcpy(N_ND->LastCalledDate,datestr);
        strcpy(N_ND->LastCalledTime,timestr);

        HBBS_SaveCallsData();
        UpdatePrivateData();


        UpdateInfoWin(); // updates calls number in the window...

        GoSystemDoor("CheckUser","LOGON");

        if (stricmp(N_ND->DoorReturn,"LOGIN_DENIED")==0)
        {
          HBBS_AddToCallersLog("Login Denied by the Check User door!");
        }
        else
        {

          sprintf(tmpstr,"Speed_%s",N_ND->ConnectBaud);
          GoSystemDoor(tmpstr,NULL);

          if (N_ND->OnlineStatus==OS_ONLINE)
          {

            if (N_ND->User.CallData.Status!=USER_NEW)
            {
              DisplaySpecialScreen("LoginLogo");
            }

            if (N_ND->OnlineStatus==OS_ONLINE)
            {

              GoUserDoor("WALL",NULL);

              if (N_ND->OnlineStatus==OS_ONLINE)
              {

                DisplaySpecialScreen("Bulletin");

                // check to see if user want's to keep tagged files from last visit
                // if he left with tagged files or lost carrier with files still tagged

                GoSystemDoor("CHECKTAGS",NULL);

                GoSystemDoor("MAILSCAN",NULL);


                sprintf(tmpstr,"HBBS:System/Data/Users/%d/%d.TXT",N_ND->User.CallData.UserID,N_ND->User.CallData.UserID);
                DisplayScreen(tmpstr);

                confnum=N_ND->User.CallData.LastConf;
                if (N_ND->User.CallData.PreferedConf) confnum=N_ND->User.CallData.PreferedConf;
                sprintf(confstr,"%d",confnum);
                GoSystemDoor("JOINCONF",confstr);


                GoSystemDoor("CMDPROMPT",NULL);

                // Normally a user is only logged off a) by the sysop pressing F10 or b) by
                // typing G at the CMDPROMPT door which then calls the logoff door. but if
                // there is a problem with starting the CMDPROMPT door then we'll have to force
                // logout the user by trying to run the LOGOUT door, which should logoff the
                // user

                if (N_ND->OnlineStatus==OS_ONLINE)
                {
                  GoUserDoor("G","SYSTEM");
                }
              }
            }
          }

          strcpy(N_ND->Action,"Scripts");
          DOOR_UpdateNodeStatus(UPD_ACTION);

          // do the logoff scripts, but only if a user actually logged in..
          // and unlike /X this gets called even if you do a RL
          // so all your bulletins actually get updated!

          sprintf(tmpstr,"execute HBBS:Scripts/LogOff%ld.script %ld %ld",N_ND->NodeNum,N_ND->NodeNum,N_ND->User.CallData.UserID);
          HBBS_RunDOSCMD(tmpstr,FALSE);

          sprintf(tmpstr,"execute HBBS:Scripts/LogOff_Global.script %ld %ld",N_ND->NodeNum,N_ND->User.CallData.UserID);
          HBBS_RunDOSCMD(tmpstr,FALSE);
        }
      }
    }
    else
    {
      HBBS_AddToCallersLog("A user never logged in.  A carrier loss or timeout occured");
    }
    if (N_ND->User.Valid)
    {
      strcpy(tmpstr,"Actions: ");
      loop2=strlen(tmpstr);;
      for (loop=0;loop<LEN_ACTIONS;loop++)
      {
        if (N_ND->Actions[loop]!=ACTC_NONE)
        {
          tmpstr[loop2++]=N_ND->Actions[loop];
        }
      }
      tmpstr[loop2]=0;
      HBBS_AddToCallersLog(tmpstr);

      sprintf(tmpstr,"Totals: FU=%d FD=%d BU=%0.0f BD=%0.0f PM=%d NK=%d MR=%d MW=%d",
              N_ND->User.FilesUploaded,
              N_ND->User.FilesDownloaded,
              N_ND->User.BytesUploaded,
              N_ND->User.BytesDownloaded,
              N_ND->User.PagesMade,
              N_ND->User.NukedFiles,
              N_ND->User.MsgsRead,
              N_ND->User.MsgsWritten);

      HBBS_AddToCallersLog(tmpstr);
      sprintf(tmpstr,"Time Online: %ld",HBBS_TimeOnline());
      HBBS_AddToCallersLog(tmpstr);
    }

    HBBS_AddToCallersLog("End Of Session");


    FreeData();
    if (stricmp(N_ND->DoorReturn,"RELOGIN")==0)
    {
      ReLogin=TRUE;
      N_ND->OnlineStatus=OS_ONLINE;
      HBBS_AddToCallersLog("User is re-logging in!");
    }
    else
    {
      ReLogin=FALSE;
    }

  } while(ReLogin);

  if (!CarrierLost() && N_ND->LoginType==LOGIN_REMOTE) HangUp();
}

void ResetNodeVars( void )
{
  ResetVars();
  // this is called just before the await connect door is called..
  strcpy(N_ND->ConnectBaud,"--------");
  DOOR_UpdateNodeStatus(UPD_ACTION);
  DOOR_UpdateNodeStatus(UPD_CPSBAUD);
  HBBS_SetAccess(); // clear access levels
}

BOOL CheckModem( void )
{
  BOOL retval=TRUE,Done=FALSE;
  if (N_ND->SerOK)
  {
    do
    {
      SerReset();
      if (SerWriteStrWithTimeout("AT\n\r",5,0))
      {
        Delay(N_ND->NodeDevice.DelayBetweenCmds);
        Done=TRUE;
      }
      else
      {
        Done=((rtEZRequest("Modem Not Responding","Retry|Cancel",NULL,(struct TagItem *)&rttags,NULL)) ? FALSE : TRUE);
        if (Done) retval=FALSE;
      }
      SerReset();
    } while (!Done);
  }
  return(retval);
}

void AwaitConnect( void )
{
  BOOL Error=FALSE;

  ResetNodeVars();

  if ((N_ND->NodeSettings.UseDevice==FALSE) || ((N_ND->NodeDevice.NullModemCable==FALSE) && CheckModem()) || (N_ND->NodeDevice.NullModemCable==TRUE))
  {
    do
    {
      if (!N_ND->NodeDevice.NullModemCable) InitModem();
      N_ND->RunningAwait=TRUE;
      Error=!GoSystemDoor("AWAIT",NULL);                 // GoSys returns true if door loads OK
      N_ND->RunningAwait=FALSE;

      // if local login then put modem offhook so no-one else calls...
      if (N_ND->LoginType==LOGIN_LOCAL)
      {
        if (!N_ND->NodeDevice.NullModemCable || !N_ND->NodeSettings.UseDevice) OffHook();
        strcpy(N_ND->ConnectBaud,"LOCAL");
      }

      DOOR_UpdateNodeStatus(UPD_CPSBAUD);
      if (N_ND->LoginType!=LOGIN_NONE)
      {
        if (HistoryList=HBBS_CreateList())
        {
          HistoryItems=0;

          SendStatus(STAT_ONLINE);
          N_ND->OnlineStatus=OS_ONLINE;

          GoBBS();

          N_ND->LoginType=LOGIN_NONE;

          FreeStrList(HistoryList);
          HistoryList=NULL;
          HistoryItems=0;

          HBBS_FreeListNodes(N_ND->OLMList);
          N_ND->OLMCount=0;

          if (N_ND->NodeFlags & NFLG_OLMSWAITING) N_ND->NodeFlags-=NFLG_OLMSWAITING;

          SendStatus(STAT_READY);
        }

      }
      ResetNodeVars();
    } while (N_ND->RequestShutdown==FALSE && !Error);
    if (!N_ND->NodeDevice.NullModemCable && N_ND->NodeSettings.UseDevice) OffHook();
  } // else do error message
  if (Error)
  {
    HBBS_rterror("Error running door, \"Await\"");
  }
}

void HandleDoorIOMsg(struct DoorIOMsg *DMsg)
{
  DMsg->ReturnVal=0;
  switch(DMsg->Status)
  {
    case DOORIO_WRITECONSTR:
      ConWriteStr(DMsg->Data);
      break;
    case DOORIO_WRITECONDATA:
      ConWriteData(DMsg->Data,DMsg->DataLength);
      break;
    case DOORIO_WRITESERSTR:
      SerWriteStr(DMsg->Data);
      break;
    case DOORIO_WRITESERDATA:
      SerWriteData(DMsg->Data,DMsg->DataLength);
      break;
    case DOORIO_WRITESTR:
      PutText(DMsg->Data);
      break;
    case DOORIO_GETLINE:
      DMsg->ReturnVal=Get_Line(DMsg->Flags,DMsg->Data[0],DMsg->Num1,DMsg->Num2,DMsg->OptionStr);
      break;
    case DOORIO_CHANGECONSOLEMODE:
      HBBS_ChangeConsoleMode(DMsg->Num1);
      break;
    case DOORIO_SYSTEMDOOR:
      DMsg->ReturnVal=GoSystemDoor(DMsg->Data,DMsg->OptionStr);
      break;
    case DOORIO_USERDOOR:
      DMsg->ReturnVal=GoUserDoor(DMsg->Data,DMsg->OptionStr);
      break;
    case DOORIO_HANGUP:
      strcpy(N_ND->Action,"Hangup");
      DOOR_UpdateNodeStatus(UPD_ACTION);
      if (N_ND->LoginType==LOGIN_REMOTE) HangUp();
      N_ND->OnlineStatus=OS_OFFLINE;
      break;
    case DOORIO_DISPLAYSCREEN:
      DMsg->ReturnVal=DisplayScreen(DMsg->Data);
      break;
    case DOORIO_DISPLAYSPECIALSCREEN:
      DMsg->ReturnVal=DisplaySpecialScreen(DMsg->Data);
      break;
    case DOORIO_PAUSEPROMPT:
      PausePrompt(DMsg->Data,0);
      break;
    case DOORIO_CONTINUEPROMPT:
      DMsg->ReturnVal=ContinuePrompt(DMsg->Data,DMsg->Flags,0);
      break;
    case DOORIO_ACONTINUEPROMPT:
      DMsg->ReturnVal=ContinuePrompt(DMsg->Data,DMsg->Flags,DMsg->Num1);
      break;
    case DOORIO_ADDLASTUPLOAD:
      Add_Last_Upload(DMsg->Data);
      break;
    case DOORIO_ADDLASTDOWNLOAD:
      Add_Last_Download(DMsg->Data);
      break;
    case DOORIO_MENUPROMPT:
      MenuPrompt(DMsg->Data,DMsg->OptionStr[0]);
      break;
    case DOORIO_CHECKRAW:
      DMsg->ReturnVal=CheckRaw(DMsg->Flags);
      break;
    case DOORIO_CHECKCSI:
      DMsg->ReturnVal=CheckCSI(DMsg->Data[0],DMsg->Num1);
      break;
  }
  // a door message MAY be sent to the node without the control or other program
  // wanting to be told that the NODE has recived the message, all messages sent
  // that DONT want a reply WILL be freed..

  if (DMsg->message.mn_ReplyPort)
  {
    ReplyMsg((struct Message *)DMsg);
  }
  else
  {
    FreeVec(DMsg);
  }
}
