/*

  todo:

  * add history to Get_Line()
  * add timeout to RunDoor()

  done:

  moved N_ND->DoorReturn[0]=0; from godoor() to systemdoor() and userdoor()
  in case a door was supposed to run, but was not setup in the commands
  files yet..

  moved N_ND->ActiveDoor=NULL; from rundoor to godoor() as CLI doors would
  then cause the node to think the door had crashed :-)


*/

#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 <time.h>
#include <libraries/reqtools.h>

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

#include "NodeGUI.h"

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

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

#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"
#include "/library/hbbscommon_protos.h"
#include "/library/hbbscommon_pragmas.h"
#include "nodelibrary/hbbsnode_protos.h"
#include "nodelibrary/hbbsnode_pragmas.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));
}


#define KEY_NONE 0
#define KEY_ESCAPE 1
#define KEY_CSI 2

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)
  {
    CleanupNodeConsoleWin();
  }
  else
  if (stricmp(N_ND->csistring,str_CSI_F8)==0)
  {
    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)
    {
      if (N_ND->CurrentLine[0]==0) status=IN_DISPLAYAWAIT;            // Display Awaitscreen if no chars recieved yet..
    }
    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_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);
      }
    }
    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);
}
/*

#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

*/

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);
}


ULONG Get_Line(ULONG Flags,char PasswordChar,ULONG MaxLen, ULONG Timeout, UBYTE *PromptStr)
{
  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;
  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 (Timeout)
  {
    TD=SubmitTimer(N_ND->NodeTimer,Timeout,0);
//    if (TD) printf("Submitted a timer for %ld seconds\n",Timeout);
  }

  if (PromptStr)
  {
    strNcpy(N_ND->CurrentLine,PromptStr,MaxLen > 0 ? MaxLen : LEN_CURRENTLINE);
    if (Flags & GL_SYSOP)
    {
      PutConText(N_ND->CurrentLine);
    }
    else
    {
      PutText(N_ND->CurrentLine);
    }
    xpos=strlen(N_ND->CurrentLine);
  }

  if (MaxLen==0) MaxLen=LEN_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);

    SetUpSigs();
//    if (Timeout)
//    {
//      printf("Timersig: %x\n",N_ND->TimerSig);
//    }
    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 (Timeout)
    {
      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");
            }
          }

//          puts("Timeout!");
        }
      }
//      else
//      puts("Error, no timer data!");
    }

    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)
      {
//        puts("restarting timer!");
        if (TD) AbortTimer(N_ND->NodeTimer,TD);
        TD=SubmitTimer(N_ND->NodeTimer,Timeout,0);
//        if (TD) puts("restarted ok!");
      }
      // 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);
                    }
                  }
                }
              }

              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);
                    }
                  }
                }
              }

              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<(min(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");
//                        DeleteChars(1);
                      }
                    }
                  }
                }
              }
              else
              {
                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)) < min(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]);
                          }
                        }
                        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
                        {
                          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...
      }
    }
  };
  if (TD) AbortTimer(N_ND->NodeTimer,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];

  sprintf(cmd,"%s %d %s",doorname,N_NodeNum,options ? options : "\0");
  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)
{
  char outstr[256];
  ULONG ReturnedSigs;
  struct DoorData *CurrentDoor;
  struct TimerData *TD;
  BOOL timeout,doorstarted;
  struct MsgPort *DP;
  char *clinesave=N_ND->CurrentLine; // save old line..

  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;

                                            //*C* uncomment! make it a sysop definable setting ?
      if (TD=SubmitTimer(N_ND->NodeTimer,300/*TIMEOUT_DOORLOAD*/,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 );
            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)
            {
              // 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) puts(N_ND->DoorReturn);
            }
          }
        }
        else
        {
          if (TD) AbortTimer(N_ND->NodeTimer,TD);
        }
      }
      FreeVec(N_ND->CurrentLine);
      N_ND->CurrentLine=clinesave;
    }
  }
  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);
  }
}

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,struct Node *node)
{
  if (stricmp(actualtype,"SCREEN")==0)
  {
    DisplayScreen(node->ln_Name);
  }
  else
  {
    if (stricmp(actualtype,"SSCREEN")==0)
    {
      DisplaySpecialScreen(node->ln_Name);
    }
    else
    {
      if (stricmp(actualtype,"CLS")==0)
      {
        PutText(ANSI_CLS);
      }
      else
      {
        if (stricmp(actualtype,"PAUSE")==0)
        {
          PausePrompt(node->ln_Name,0);
        }
        else
        {
          if (stricmp(actualtype,"ECHO")==0)
          {
            PutText(node->ln_Name);
            PutText("\r\n");
          }
          else
          {
            if (stricmp(actualtype,"DOS")==0)
            {
              HBBS_RunDOSCMD(node->ln_Name,FALSE);
            }
          }
        }
      }
    }
  }
}

#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=NORMAL
  Examine_DOOR_1=HBBS:Doors/User/Examine/Examine.HBBS
  Examine_Param_1=

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

  Examine_Type_3=NORMAL
  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,"_Door");
          if (CfgFile=HBBS_LoadConfig(filename,LCFG_NONE))
          {
            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,"NORMAL")==0)
                    {
                      GoDoor(node->ln_Name,actualparam,options,DebugMode);
                    }
                    else
                    {
                      if (stricmp(actualtype,"CLI")==0)
                      {
                        RunDoor(node->ln_Name,options,DebugMode,FALSE);
                      }
                      else
                      {
                        CheckDoorTypes(actualtype,node);
                      }
                    }
                    FreeStr(actualparam);
                  }
                  FreeStr(actualtype);
                }
              }
              FreeStrList(CmdList);
            }
            HBBS_FlushConfig(CfgFile);
          }
          FreeVec(optionname);
        }
      }
    }
  }
  return(done);
}

short WhatAccessNode(V_SMALLNUM accesslevel)
{
  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))
        {
          sprintf(optionname,"%s_Door",doorname);
          if (CfgFile=HBBS_LoadConfig(filename,LCFG_NONE))
          {
            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,"NORMAL")==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);
                      }
                    }
                    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)
  {
    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_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];
  BOOL TriedAllLevels,Done=FALSE;
  struct Node *node;

  short whichdir=DSS_CONF;
  short CurrentAccess;

  do
  {
    filename[0]=0;
    if (N_ND->User.Valid)
    {
      TriedAllLevels=FALSE;
      CurrentAccess=WhatAccessNode(N_ND->User.NormalData.Access);
    }
    else
    {
      CurrentAccess=-1;
    }

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

      if (filename)
      {
        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);
        }
        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);  // *C* Change To Users Selection

        Done=DisplayScreen(filename);

      } 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;

  // create unique file name for each user..

  sprintf(tmpstr,"HBBS:System/Data/TaggedFiles/%d",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,"%d",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/TaggedFiles/%d",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))
            {

              sprintf(optionstr,"FileSize_%d",N_ND->TaggedFiles);
              if (HBBS_GetSetting(cfgfile,(void *)&filetagnode->FileSize,VTYPE_BIGNUM,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;

  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->FilesUploaded=0L;
  N_ND->FilesDownloaded=0L;
  N_ND->BytesUploaded=0L;
  N_ND->BytesDownloaded=0L;
  HBBS_SetBBSCols();
}

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

void GoBBS( void )
{
  char tmpstr[1024],confstr[10],datestr[LEN_DATESTR],timestr[LEN_TIMESTR];
  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,"************************************************[%s*", N_ND->LoginType==LOGIN_REMOTE ? "REMOTE]" : "LOCAL]*");
    HBBS_AddToCallersLog(tmpstr);


    PutText(ANSI_RESET);

    GoSystemDoor("FRONTEND",NULL);
    // 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();

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

      // update the users stats..

      N_ND->User.NormalData.CallsMade++;
      N_ND->User.CallData.CallsMade++;
      time(&N_ND->User.CallData.LastCalledDate);
      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();

      HBBS_GetDate(datestr);
      HBBS_GetTime(timestr);

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

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

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

      HBBS_SaveCallsData();


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

      GoSystemDoor("CheckUser","LOGON");

      if (stricmp(N_ND->DoorReturn,"LOGIN_DENIED")==0)
      {
      }
      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);

            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:Screens/Users/%s.TXT",N_ND->User.CallData.Handle);
            DisplayScreen(tmpstr);  // *C* keep ?

            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");
            }
          }
        }
      }

      // 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);

    }

    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);

    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);

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

      // if local login then put modem offhook so no-one else calls...
      if (N_ND->LoginType==LOGIN_LOCAL)
      {
        if (!N_ND->NodeDevice.NullModemCable) 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) 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_SYSTEMDOOR:
      DMsg->ReturnVal=GoSystemDoor(DMsg->Data,DMsg->OptionStr);
      break;
    case DOORIO_USERDOOR:
      DMsg->ReturnVal=GoUserDoor(DMsg->Data,DMsg->OptionStr);
      break;
    case DOORIO_HANGUP:
      HangUp();
      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);
      break;
    case DOORIO_ADDLASTUPLOAD:
      Add_Last_Upload(DMsg->Data);
      break;
    case DOORIO_MENUPROMPT:
      MenuPrompt(DMsg->Data,DMsg->OptionStr[0]);
      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);
  }
}

