/****** HBBSNode.library/--Description-- *****************************
*
*   NAME
*      HBBSNode.library - Shared library for HBBS Nodes and doors.
*
*********************************************************************/

#define HBBSNODELIB
#define MAIN

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

#include <exec/exec.h>
#include <exec/types.h>

#include <dos/dos.h>
#include <dos/dostags.h>

#include <libraries/reqtools.h>

#include <intuition/intuition.h>

#include <devices/console.h>
#include <devices/conunit.h>

#include <devices/serial.h>
#include <hardware/cia.h>

#include <graphics/gfxbase.h>
#include <graphics/scale.h>


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

#include <clib/intuition_protos.h>
#include <clib/console_protos.h>
#include <clib/graphics_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/reqtools.h>
#include <pragmas/intuition_pragmas.h>



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

#include <HBBS/Hbbscommon_protos.h>
#include <HBBS/hbbscommon_pragmas_sas.h>

#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas_sas.h>

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

char *versionstr="$VER: HBBSNode.library "RELEASE_STR;


// needed libs..

struct DosLibrary *DOSBase=NULL;
struct GfxBase *GfxBase=NULL;
struct ExecBase *SysBase=NULL;
struct ReqToolsBase *ReqToolsBase=NULL;
struct Library *HBBSCommonBase=NULL;
struct IntuitionBase *IntuitionBase = NULL;

struct BBSGlobalData *BBSGlobal=NULL;
struct NodeData *N_ND=NULL;

struct DoorData C_DOOR; // current door (if active of course!)

V_BOOL DoorOK=FALSE;

// * SOME FORWARD DEFINES..

LONG __asm __saveds LIBHBBS_TimeOnline( void );
LONG __asm __saveds LIBHBBS_TimeLeft( void );


void DoorStatus(V_BIGNUM status)
{
  struct DoorActivityMsg *DMsg;

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

BOOL SendStartDoorMsg( void )
{
  struct DoorActivityMsg *DMsg;
  BOOL retval=FALSE;

  if (DMsg=AllocVec(sizeof(struct DoorActivityMsg ),MEMF_PUBLIC|MEMF_CLEAR))
  {
    DMsg->message.mn_Node.ln_Type = NT_MESSAGE;
    DMsg->message.mn_ReplyPort=C_DOOR.ReplyPort; // causes this function to wait for a reply
    DMsg->message.mn_Length=sizeof(struct DoorActivityMsg);
    DMsg->MsgType=mtype_DOORACTIVITY;
    DMsg->Status=1;
    if (SendMessage((struct Message*)DMsg,N_ND->DoorStartPortName)) retval=TRUE;
    FreeVec(DMsg);
  }
  return(retval);
}


BOOL __asm __saveds LIBHBBS_InitNode(register __d0 int N_NodeNum)
{
  SysBase = *(struct ExecBase **) 4L;
  if (DOSBase = (struct DosLibrary *) OpenLibrary (DOSNAME, 0))
  {
    if (GfxBase = (struct GfxBase *) OpenLibrary (GRAPHICSNAME, 0))
    {
      if (IntuitionBase = (struct IntuitionBase * )OpenLibrary((UBYTE *)"intuition.library" , 37))
      {
        if (ReqToolsBase = (struct ReqToolsBase *) OpenLibrary (REQTOOLSNAME, REQTOOLSVERSION))
        {
          if (HBBSCommonBase = OpenLibrary("HBBSCommon.library",HBBSCOMMONVERSION))
          {
            if (BBSGlobal=HBBS_GimmeBBS())
            {
              if (N_ND=HBBS_NodeDataPtr(N_NodeNum))
              {
                return(TRUE);
              }
            }
          }
        }
      }
    }
  }
  return(FALSE);
}

void __asm __saveds LIBHBBS_CleanUpNode( void )
{
  if (HBBSCommonBase)
  {
//    if (N_ND) HBBS_ResetNodeData(N_ND); // reset node variables
    CloseLibrary (HBBSCommonBase);
  }
  if (ReqToolsBase)   CloseLibrary ((struct Library *)ReqToolsBase);
  if (GfxBase)        CloseLibrary ((struct Library *) GfxBase);
  if (IntuitionBase ) CloseLibrary( ( struct Library * )IntuitionBase );
  if (DOSBase)        CloseLibrary ((struct Library *) DOSBase);
}

/****** HBBSNode.library/HBBS_InitDoor *******************************
*
*   NAME
*      HBBS_InitDoor - Initalises syncronisation of the current
*      program to the node.
*
*   SYNOPSIS
*      HBBS_InitDoor( NodeNum, DoorName )
*                     D0,      A0
*
*      BOOL HBBS_InitDoor(int NodeNum, char *DoorName)
*
*   FUNCTION
*      This function is to be called once during a program's
*      execution, it must be called before any other function in this
*      library is called.
*
*   INPUTS
*      NodeNum - the number of the node that this door is to run on
*      DoorName - a string.  Only MAX_ACTION_LEN chars are displayed
*          in the node's status window and WHO doors.
*
*   RESULT
*      Returns TRUE if the initalisation was sucessful
*
*   SEE ALSO
*      HBBS_CleanUpDoor()
*
*********************************************************************/

BOOL __asm __saveds LIBHBBS_InitDoor( register __d0 int NodeNum, register __a0 char *DoorName)
{
  if (LIBHBBS_InitNode(NodeNum))
  {
    C_DOOR.node.ln_Name=DoorName;

    sprintf(C_DOOR.DoorPortName,"HBBS_Node_%ldDP%ld",NodeNum,N_ND->DoorsRunning+1); // we increment door number below..

    if (C_DOOR.DoorPort=CreatePort(C_DOOR.DoorPortName,0)) // named port
    {
      if (C_DOOR.ReplyPort=CreateMsgPort()) // unnamed port
      {
        // these must be updated at the same time as we don't want another program referencing
        // ActiveDoor AND the door list and it not being correct now do we ? :-)

        Forbid();
        N_ND->ActiveDoor=&C_DOOR;
        AddHead(N_ND->DoorList,(struct Node*)&C_DOOR);
        N_ND->DoorsRunning++;
        Permit();

        // tell the NODE the door is running OK (if the node does not get a door OK message
        // within 15 seconds then it times out!)

        if (SendStartDoorMsg())
        {
          DoorStatus(DMSG_DOORSTARTED);
          // set the internal library's flag (used when we close the door down to free mem!
          DoorOK=TRUE;
          return(TRUE);
        }

      }
      else DeletePort(C_DOOR.DoorPort);
    }
  }
  return(FALSE);
}

/****** HBBSNode.library/HBBS_CleanUpDoor ****************************
*
*   NAME
*      HBBS_CleanUpDoor - Cleans up memory allocated by this library
*      that was used for the program's access to the BBS.
*
*   SYNOPSIS
*      HBBS_CleanUpDoor( void )
*
*      void HBBS_CleanUpDoor( void )
*
*   FUNCTION
*      This function is to be called once during a program's
*      execution, it must be called before the program exits, other-
*      wise the node will hang.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_InitDoor()
*
*********************************************************************/

void __asm __saveds LIBHBBS_CleanUpDoor( void )
{
  if (DoorOK)
  {
    DeleteMsgPort(C_DOOR.ReplyPort); // unnnamed port
    DeletePort(C_DOOR.DoorPort);     // named port
    Forbid();
    // remove ourself from the doorlist

    RemHead(N_ND->DoorList);

    N_ND->DoorsRunning--;

    // set the active door to the door that called us (if we were spawned by another door)
    // if not, set activedoor to null

    if (N_ND->DoorList->lh_Head->ln_Succ) // more doors ?
    {
      N_ND->ActiveDoor=(struct DoorData *)N_ND->DoorList->lh_Head;
    }
    else N_ND->ActiveDoor=NULL;

    Permit();


    // tell the node that we've finished!
    DoorStatus(DMSG_DOORFINISHED);

  }
  LIBHBBS_CleanUpNode();
}
void  __asm __saveds LIBConWriteData(register __a0 UBYTE *data,register __d0 ULONG length)
{
  if (N_ND->ConOK && data && length>=0)
  {
    N_ND->ConWrite->io_Command  = CMD_WRITE;
    N_ND->ConWrite->io_Data     = data;
    N_ND->ConWrite->io_Length   = length;

    DoIO((struct IORequest *)N_ND->ConWrite);
  }
}

void  __asm __saveds LIBConWriteStr(register __a0 UBYTE *data)
{
  if (N_ND->ConOK && data)
  {
    N_ND->ConWrite->io_Command  = CMD_WRITE;
    N_ND->ConWrite->io_Data     = data;
    N_ND->ConWrite->io_Length   = -1;

    DoIO((struct IORequest *)N_ND->ConWrite);
  }
}

void  __asm __saveds LIBAbortConRead( void )
{
  if (N_ND->ConWaiting)
  {
    if (!CheckIO((struct IORequest*)N_ND->ConRead))
    {
      AbortIO((struct IORequest*)N_ND->ConRead);
      WaitIO((struct IORequest*)N_ND->ConRead);
    }
    N_ND->ConWaiting=FALSE;
  }
}

void  __asm __saveds LIBSendConReadData( void )
{
  LIBAbortConRead();
  N_ND->ConRead->io_Command  = CMD_READ;
  N_ND->ConRead->io_Data     = (APTR)N_ND->ConBuffer;
  N_ND->ConRead->io_Length   = N_ND->ConBufferLen;

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

void  __asm __saveds LIBConReadData(register __d0 ULONG Length ) // make DAMN sure that length is NEVER more that the buffer size!
{
  LIBAbortConRead();
  N_ND->ConRead->io_Command  = CMD_READ;
  N_ND->ConRead->io_Data     = (APTR)N_ND->ConBuffer;
  N_ND->ConRead->io_Length   = Length;

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

void  __asm __saveds LIBConWaitData( void )
{
  if (!N_ND->ConWaiting) LIBSendConReadData();
  WaitIO((struct IORequest*)N_ND->ConRead);
  N_ND->ConWaiting=FALSE;
  N_ND->ConBytes=N_ND->ConRead->io_Actual;
}

// aborts an iorequest that has been sent

void __asm __saveds LIBAbortSerRead( void )
{
  if (N_ND->SerWaiting)
  {
    if (!CheckIO((struct IORequest*)N_ND->SerRead))
    {
      AbortIO((struct IORequest*)N_ND->SerRead);
    }
    WaitIO((struct IORequest*)N_ND->SerRead); // to tidy up..
    N_ND->SerWaiting=FALSE;
  }
}

// SendIO()'s a request to read 1 bye

void __asm __saveds LIBSendSerReadData( void )
{
  N_ND->SerRead->IOSer.io_Command  = CMD_READ;
  N_ND->SerRead->IOSer.io_Data     = (APTR)N_ND->SerBuffer;
  N_ND->SerRead->IOSer.io_Length   = 1;

  ClrSignal(1L << N_ND->SerPort->mp_SigBit);
  SendIO((struct IORequest*)N_ND->SerRead);
  N_ND->SerWaiting=TRUE;
}

void __asm __saveds LIBSerWaitData( void )
{
  if (!N_ND->SerWaiting) LIBSendSerReadData();
  WaitIO((struct IORequest*)N_ND->SerRead);
  N_ND->SerBytes=N_ND->SerRead->IOSer.io_Actual;
  N_ND->SerWaiting=FALSE;
}

// SendIO()'s a request to read a block of data

void __asm __saveds LIBSendSerReadBlock(register __a0 UBYTE *data,register __d0 ULONG length )
{
  LIBAbortSerRead(); // *C* not sure about this one..  might slowdown throughput..

  N_ND->SerRead->IOSer.io_Command  = CMD_READ;
  N_ND->SerRead->IOSer.io_Data     = (APTR)data;
  N_ND->SerRead->IOSer.io_Length   = length;

  ClrSignal(1L << N_ND->SerPort->mp_SigBit);
  SendIO((struct IORequest*)N_ND->SerRead);
  N_ND->SerWaiting=TRUE;
}

// DioIO()'s a request to read a block of data

void __asm __saveds LIBWaitSerReadBlock(register __a0 UBYTE *data,register __d0 ULONG length )
{
  LIBSendSerReadBlock(data,length);
  LIBSerWaitData();
}

// DoIO()'s a Query!

ULONG __asm __saveds LIBSerQueryData( void )
{
  N_ND->SerWrite->IOSer.io_Command  = SDCMD_QUERY;
  DoIO((struct IORequest*)N_ND->SerWrite);
  return(N_ND->SerWrite->IOSer.io_Actual);
}

ULONG __asm __saveds LIBSerClear( void )
{
  N_ND->SerWrite->IOSer.io_Command  = CMD_CLEAR;
  DoIO((struct IORequest*)N_ND->SerWrite);
  return(N_ND->SerWrite->IOSer.io_Error);
}

/****** HBBSNode.library/CarrierLost *********************************
*
*   NAME
*      CarrierLost - Checks to see if the carrier has been lost
*
*   SYNOPSIS
*      CarrierLost( void )
*
*      V_BOOL CarrierLost( void )
*
*   FUNCTION
*      This function is used to check if the carrier is lost.
*      Normally, The node's NodeData->OnlineStatus will be
*      set to OS_ONLINE or OS_ONLINE when carrier is lost
*      So you should normally check that, instead of using this
*      function.
*
*      However, if your door displays lots of output without
*      asking for much input then you should use this function.
*
*   INPUTS
*      None
*
*   RESULT
*      TRUE if carrier has been lost.
*
*   SEE ALSO
*      defines.h/struct NodeData
*
*********************************************************************/

V_BOOL __asm __saveds LIBCarrierLost( void )
{
  if (N_ND->LoginType==LOGIN_REMOTE && !N_ND->NodeDevice.NullModemCable)
  {
    if (N_ND->NodeSettings.UseDevice)
    {
      LIBSerQueryData();
      return((N_ND->SerWrite->io_Status & 1L << CIAB_COMCD) ? (V_BOOL)TRUE : (V_BOOL)FALSE);
    }
  }
  return((V_BOOL)FALSE);
}

// writes a block of data to the serial port, checking for a timeout
// (specified in seconds,micros) and returns FALSE if a timeout occured before all
// the data has been sent, or there was a memory error.

BOOL __asm __saveds LIBSerWriteDataWithTimeout(register __a0 UBYTE *data,register __d0 ULONG length,register __d1 ULONG Seconds,register __d2 ULONG Micros)
{
  BOOL retval=FALSE;
  struct TimerData *TD;

  if (TD=SubmitTimer(N_ND->NodeTimer,Seconds,Micros))
  {
    if (data && length>0)
    {
      LIBAbortSerRead();
      N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
      N_ND->SerWrite->IOSer.io_Data     = data;
      N_ND->SerWrite->IOSer.io_Length   = length;

      SendIO((struct IORequest *)N_ND->SerWrite);

      // ok, wait for the timer or serial device to tell us sommat..
      Wait (DEF_TIMERSIG | DEF_SERSIG);

      // ok sommat hapenned. lets check the timer..
      if (CheckTimer(N_ND->NodeTimer,TD))
      {
        // yup, timeout occured, so abort the serial write request..
        AbortIO((struct IORequest *)N_ND->SerWrite);
        TD=NULL;
      }
      else
      {
        // timer not done yet, so it must be the serial port that finished..
        retval=TRUE;
      }
      // and then tidy up..
      WaitIO((struct IORequest *)N_ND->SerWrite);
    }
    if (TD) AbortTimer(N_ND->NodeTimer,TD);
  }
  return(retval);
}

// writes a block of data to the serial port without checking for a timeout..

void __asm __saveds LIBSerWriteData(register __a0 UBYTE *data,register __d0 ULONG length)
{
  if (data && length>0)
  {
    LIBAbortSerRead();
    N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
    N_ND->SerWrite->IOSer.io_Data     = data;
    N_ND->SerWrite->IOSer.io_Length   = length;

    DoIO((struct IORequest *)N_ND->SerWrite);
  }
}

BOOL __asm __saveds LIBSerWriteStrWithTimeout(register __a0 UBYTE *str,register __d0 ULONG Seconds,register __d1 ULONG Micros)
{
  BOOL retval=FALSE;
  struct TimerData *TD;

  if (TD=SubmitTimer(N_ND->NodeTimer,Seconds,Micros))
  {
    if (str && str[0])  // str valid pointer ? and does it contain data ??
    {
      LIBAbortSerRead();
      N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
      N_ND->SerWrite->IOSer.io_Data     = str;
      N_ND->SerWrite->IOSer.io_Length   = strlen(str);

      ClrSignal(DEF_TIMERSIG | DEF_SERSIG);

      SendIO((struct IORequest *)N_ND->SerWrite);

      // ok, wait for the timer or serial device to tell us sommat..
      Wait (DEF_TIMERSIG | DEF_SERSIG);

      // ok sommat hapenned. lets check the timer..
      if (CheckTimer(N_ND->NodeTimer,TD))
      {
        TD=NULL;
        // yup, timeout occured, so abort the serial write request..
        AbortIO((struct IORequest *)N_ND->SerWrite);
      }
      else
      {
        // timer not done yet, so it must be the serial port that finished..
        retval=TRUE;
        // and then tidy up..
      }
      WaitIO((struct IORequest *)N_ND->SerWrite);
    }
    if (TD) AbortTimer(N_ND->NodeTimer,TD);
  }
  return(retval);
}

// writes a string to the serial device..

void __asm __saveds LIBSerWriteStr(register __a0 UBYTE *str)
{
  if (str && str[0])   // str valid pointer ? and does it contain data ??
  {
    LIBAbortSerRead();
    N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
    N_ND->SerWrite->IOSer.io_Data     = str;
    N_ND->SerWrite->IOSer.io_Length   = strlen(str);

    // hehe LamiExpress sets io_Length to strlen(str).. LAME! ;-)
    // if they read the docs they might see that setting io_Length to -1
    // makes the device output the contents of io_Data until it encounters
    // a null terminator! :-)

    // ah but perhaps with good reason !?!
    // the C= serial.device outputs the /0 and then stops instead of
    // stopping without outputting the /0

    DoIO((struct IORequest *)N_ND->SerWrite);
  }
}


// writes a string to the serial device.. but replaces ~'s with delays!

void __asm __saveds LIBSerDelayWriteStr(register __a0 UBYTE *str)
{
  int loop=0;
  if (str && str[0])   // str valid pointer ? and does it contain data ??
  {
    LIBAbortSerRead();

    while (str[loop])
    {
      if (str[loop]=='~')
      {
        Delay(N_ND->NodeDevice.TildeDelay);
      }
      else
      {
        N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
        N_ND->SerWrite->IOSer.io_Data     = &str[loop];
        N_ND->SerWrite->IOSer.io_Length   = 1;
        DoIO((struct IORequest *)N_ND->SerWrite);
      }
      loop++;
    }

  }
}

// writes a char to the serial device..

void __asm __saveds LIBSerWriteChar(register __d0 UBYTE c)
{
  char ch=c;

  LIBAbortSerRead();
  N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
  N_ND->SerWrite->IOSer.io_Data     = &ch;
  N_ND->SerWrite->IOSer.io_Length   = 1;
  DoIO((struct IORequest *)N_ND->SerWrite);
}

void  __asm __saveds LIBPutText(register __a0 UBYTE *str)
{
  // generic routine for all string output, this determines wether or not
  // to write to the console or serial ports..

  if (str && str[0])
  {
    if (N_ND->ConOK)
    {
      N_ND->ConWrite->io_Command  = CMD_WRITE;
      N_ND->ConWrite->io_Data     = str;
      N_ND->ConWrite->io_Length   = -1;
      DoIO((struct IORequest *)N_ND->ConWrite);
    }
    if ((N_ND->LoginType==LOGIN_REMOTE) && (!(N_ND->NodeFlags & NFLG_BLOCKSERIAL)))
    {
      LIBAbortSerRead();
      N_ND->SerWrite->IOSer.io_Command  = CMD_WRITE;
      N_ND->SerWrite->IOSer.io_Data     = str;
      N_ND->SerWrite->IOSer.io_Length   = strlen(str);
      DoIO((struct IORequest *)N_ND->SerWrite);
    }
  }
}

void  __asm __saveds LIBPutData(register __a0 UBYTE *data,register __d0 ULONG Length)
{
  // generic routine for all tring output, this determines wether or not
  // to write to the console or serial ports..

  if (N_ND->ConOK) LIBConWriteData(data,Length);
  if ((N_ND->LoginType==LOGIN_REMOTE)  && (!(N_ND->NodeFlags & NFLG_BLOCKSERIAL))) LIBSerWriteData(data,Length);
}

void  __asm __saveds LIBPutChar(register __d0 UBYTE ch)
{
  char c=ch;
  // generic routine for all tring output, this determines wether or not
  // to write to the console or serial ports..
  if (N_ND->ConOK) LIBConWriteData(&c,1);
  if ((N_ND->LoginType==LOGIN_REMOTE) && (!(N_ND->NodeFlags & NFLG_BLOCKSERIAL))) LIBSerWriteData(&c,1);
}

void  __asm __saveds LIBPutConText(register __a0 UBYTE *str)
{
  // generic routine for all tring output, this determines wether or not
  // to write to the console or serial ports..

  if (N_ND->ConOK) LIBConWriteStr(str);
}

void  __asm __saveds LIBPutConData(register __a0 UBYTE *data,register __d0 ULONG Length)
{
  // generic routine for all tring output, this determines wether or not
  // to write to the console or serial ports..

  if (N_ND->ConOK) LIBConWriteData(data,Length);
}

void  __asm __saveds LIBPutConChar(register __d0 UBYTE ch)
{
  char c=ch;
  // generic routine for all tring output, this determines wether or not
  // to write to the console or serial ports..
  if (N_ND->ConOK) LIBConWriteData(&c,1);
}

ULONG __asm __saveds LIBSetupConSerSigs( void )
{
  N_ND->ConSig=0L;
  N_ND->ConWinSig=0L;

  if (N_ND->ConOK)
  {
    if (N_ND->ConWaiting) N_ND->ConSig=DEF_CONSIG;
    N_ND->ConWinSig=DEF_CONWINSIG;
  }

  // only check serial if remote login.. OR if no-one's connected yet..

  if (N_ND->LoginType !=LOGIN_LOCAL && N_ND->SerOK && N_ND->SerWaiting)
    N_ND->SerSig=DEF_SERSIG;
  else
    N_ND->SerSig=0;

  return(N_ND->SerSig | N_ND->ConSig);
}

ULONG __asm __saveds LIBHandleConSigs(register __d0 ULONG ReturnedSigs)
{
  // a calling routine should call this until it returns false
  // this is cos you might get two signals at the same time
  // and you don't want to miss anything!

  if (ReturnedSigs & N_ND->ConSig && N_ND->ConOK && N_ND->ConWaiting)
  {
    LIBConWaitData();
    N_ND->IBuffer=N_ND->ConBuffer;
    N_ND->IBytes=N_ND->ConBytes;
    return(TRUE);
  }
  return(FALSE);
}

ULONG __asm __saveds LIBHandleSerSigs(register __d0 ULONG ReturnedSigs)
{
  // a calling routine should call this until it returns false
  // this is cos you might get two signals at the same time
  // and you don't want to miss anything!


  if (ReturnedSigs & N_ND->SerSig && N_ND->SerOK && N_ND->SerWaiting)
  {
    LIBSerWaitData();
    N_ND->IBuffer=N_ND->SerBuffer;
    N_ND->IBytes=N_ND->SerBytes;
    return(TRUE);
  }
  return(FALSE);
}

ULONG __asm __saveds LIBHandleConSerSigs(register __d0 ULONG ReturnedSigs)
{
  // a calling routine should call this until it returns false
  // this is cos you might get two signals at the same time
  // and you don't want to miss anything!

  if (LIBHandleConSigs(ReturnedSigs) || LIBHandleSerSigs(ReturnedSigs))
  {
    return(TRUE);
  }
  else return(FALSE);

/*  if (ReturnedSigs & N_ND->ConSig && N_ND->ConOK && N_ND->ConWaiting)
  {
    LIBConWaitData();
    N_ND->IBuffer=N_ND->ConBuffer;
    N_ND->IBytes=N_ND->ConBytes;
    return(TRUE);
  }

  if (ReturnedSigs & N_ND->SerSig && N_ND->SerOK && N_ND->SerWaiting)
  {
    LIBSerWaitData();
    N_ND->IBuffer=N_ND->SerBuffer;
    N_ND->IBytes=N_ND->SerBytes;
    return(TRUE);
  }
  return(FALSE);
*/
}

ULONG SendDoorIOMessage(ULONG Status,UBYTE *Data,ULONG DataLength,ULONG Flags,UBYTE *OptionStr,ULONG Num1,ULONG Num2)
{
  struct DoorIOMsg *NewIOMsg=NULL;
  ULONG Result = 0;
  struct MsgPort *replyport;


  if (NewIOMsg=(struct DoorIOMsg*)AllocVec(sizeof(struct DoorIOMsg),MEMF_PUBLIC|MEMF_CLEAR))
  {
    NewIOMsg->message.mn_Node.ln_Type = NT_MESSAGE;
    NewIOMsg->message.mn_ReplyPort=C_DOOR.ReplyPort;
    replyport = C_DOOR.ReplyPort;
    NewIOMsg->message.mn_Length=sizeof(struct DoorIOMsg);
    NewIOMsg->MsgType=mtype_DOORIO;
    NewIOMsg->Status=Status;
    NewIOMsg->Data=Data;
    NewIOMsg->DataLength=DataLength;
    NewIOMsg->ReturnVal=0;
    NewIOMsg->Flags=Flags;
    NewIOMsg->OptionStr=OptionStr;
    NewIOMsg->Num1=Num1;
    NewIOMsg->Num2=Num2;

    PutMsg(N_ND->NodePort,(struct Message *)NewIOMsg);


    if (replyport) // check our saved pointer to avoid accessing possibly free'd memory
    {
      if (1L << NewIOMsg->message.mn_ReplyPort->mp_SigBit==Wait(1L << NewIOMsg->message.mn_ReplyPort->mp_SigBit))
      {
        GetMsg(NewIOMsg->message.mn_ReplyPort);
        Result = NewIOMsg->ReturnVal;
      }

      FreeVec(NewIOMsg);
      return(Result);
    }
  }

  return(0);
}

ULONG QuickDoorIOMessage(ULONG Status,UBYTE *Data)
{
  // optimized send message for stuff that only uses Data and Datalen

  ULONG retval=0;
  struct DoorIOMsg *NewIOMsg;
  if (NewIOMsg=(struct DoorIOMsg*)AllocVec(sizeof(struct DoorIOMsg),MEMF_PUBLIC|MEMF_CLEAR))
  {
    NewIOMsg->message.mn_Node.ln_Type = NT_MESSAGE;
    NewIOMsg->message.mn_ReplyPort=C_DOOR.ReplyPort;
    NewIOMsg->message.mn_Length=sizeof(struct DoorIOMsg);
    NewIOMsg->MsgType=mtype_DOORIO;
    NewIOMsg->Status=Status;
    NewIOMsg->Data=Data;

    PutMsg(N_ND->NodePort,(struct Message*)NewIOMsg);

    if (1L << C_DOOR.ReplyPort->mp_SigBit==Wait(1L << C_DOOR.ReplyPort->mp_SigBit))
    {
      GetMsg(NewIOMsg->message.mn_ReplyPort);
      retval=NewIOMsg->ReturnVal;
    }
    else retval=1L;

    FreeVec(NewIOMsg);
    return(retval);

  }
  return(0);
}

/****** HBBSNode.library/DOOR_SysopText ******************************
*
*   NAME
*      DOOR_SysopText - Outputs text to the sysop's watch window.
*
*   SYNOPSIS
*      DOOR_SysopText( str )
*                      A0
*
*      void DOOR_SysopText( UBYTE *str )
*
*   FUNCTION
*      This function writes str to the node's watch window, this lets
*      you display text/ansi that only the sysop can see.
*
*   INPUTS
*      str - a null terminated string.
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_WriteText(), DOOR_WriteSerText()
*
*********************************************************************/

void __asm __saveds LIBDOOR_SysopText(register __a0 UBYTE *str)
{
  QuickDoorIOMessage(DOORIO_WRITECONSTR,str);
}

/****** HBBSNode.library/DOOR_WriteText ******************************
*
*   NAME
*      DOOR_WriteText - Outputs text/ansi
*
*   SYNOPSIS
*      DOOR_WriteText( str )
*                      A0
*
*      void DOOR_WriteText( UBYTE *str )
*
*   FUNCTION
*      This function writes str to the node's watch window, and to
*      the serial device.
*
*   INPUTS
*      str - a null terminated string.
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_SysopText(), DOOR_WriteSerText()
*
*********************************************************************/

void __asm __saveds LIBDOOR_WriteText(register __a0 UBYTE *str)
{
  QuickDoorIOMessage(DOORIO_WRITESTR,str);
}

/****** HBBSNode.library/DOOR_WriteSerText ***************************
*
*   NAME
*      DOOR_WriteSerText - Outputs text/ansi to the serial device
*
*   SYNOPSIS
*      DOOR_WriteSerText( str )
*                         A0
*
*      void DOOR_WriteSerText( UBYTE *str )
*
*   FUNCTION
*      This function writes str to the serial device, but not the
*      node's watch window, so the sysop can't see it.
*
*      Most doors will never need to use this function.
*
*   INPUTS
*      str - a null terminated string.
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_SysopText(), DOOR_WriteText()
*
*********************************************************************/

void __asm __saveds LIBDOOR_WriteSerText(register __a0 UBYTE *str)
{
  QuickDoorIOMessage(DOORIO_WRITESERSTR,str);
}

/****** HBBSNode.library/DOOR_GetLine ********************************
*
*   NAME
*      DOOR_GetLine - Gets input from the sysop/user
*
*   SYNOPSIS
*      DOOR_GetLine( Flags, PasswordChar, MaxLen, Timeout, PromptStr )
*                    D0     D1            D2      D3       A0
*
*      void DOOR_GetLine( ULONG Flags, char PasswordChar,
*          ULONG MaxLen, ULONG Timeout, UBYTE *PromptStr)
*
*   FUNCTION
*      This function is used to get input, and not actually only
*      a line of text from the user/sysop.
*
*      It is an extremly powerful function and you should learn all
*      of it's (simple) modes of operation otherwise you might end
*      up re-inventing the wheel, so to speak.
*
*   INPUTS
*      Flags - a ULONG of logically OR'd flags.
*
*          GL_NONE - Specify this to accept all defaults
*              Most of the time you'd never want to use the defaults
*              as nothing will be displayed on the screen when the
*              user presses a key.
*
*          GL_HISTORY - Specifying this enables the user to use the
*              cursor up/down keys for command line history
*
*          GL_LINEWRAP - Use this for line based editors, if the user
*              inputs a line of text that is the same length as maxlen
*              then the last word (seperated by a ' ' character) will
*              be copied to NodeData->CurrentLineWrap, which you can
*              then copy to a temporary string and then pass back to
*              DOOR_GetLine as the PromptStr paramater
*              You must specify GL_IMMEDIATE for this to work correctly
*
*          GL_IMMEDIATE - Specify this if you want the function to
*              return as soon as the uer has entered a string that is
*              maxlen characters long.
*
*              If you use this flag with a maxlen of 1, you can use it
*              for hotkeys in menus!
*
*          GL_NORETURN - Specify this to make sure the BBS does not
*              output a "cr+lf" when a) the string is maxlen characters
*              long, or b) timeout a timeout occurs, or c) return is
*              pressed.
*
*              This is very useful when you don't want the on-screen
*              ansi disturbed or when you're implementing hotkeys in
*              a menu.
*
*          GL_EDIT - This enables the cursor left/right and backspace
*              and delete keys.
*
*          GL_DISPLAY - Using this flag makes the BBS output the
*              keypresses, if you don't use this flag nothing will
*              be displayed!
*
*              If you specified a non-zero character for PasswordChar
*              then keypresses will appear as that character instead
*              of the key that was actually pressed.
*
*              Not specifying this option is really only used when
*              implementing things like: "Press [Return]" and hotkeys
*
*          GL_SYSOP - If this flag is specified then the user's key-
*              presses are ignored, input can only come from the sysop
*
*              This is most often used for things like the account
*              editor, when validating an user that is on-line.
*
*          GL_USECHARS - This option makes GetLine only accept certain
*              key-presses.
*
*              If you copy a string to NodeData->CharsAllowed then
*              only characters that are present in that string can be
*              used as input.
*              If a user presses a key that's not in that string then
*              they will be sent a BEL character, which normally makes
*              a sound or makes their screen flash.
*
*              This function is really handy for limiting the keys
*              that a user can press when implementing hotkeys or menu
*              options.  It can also save on the error checking that
*              you'd normally have to do.
*
*          GL_NOBEEP - If specified with GL_USECHARS then a BEL
*              character will NOT be sent to the user when they press
*              an invalid key. (that doesn't appear in
*              NodeData->CharsAllowed
*
*          GL_NOOLM - Short for NO OnLine Message.
*              If this is specified then OLM's that are sent to the
*              node will not be displayed until another call to
*              GetLine is made without this flag.
*
*          GL_NODISTURB - Specifying this flag makes sure the ansi to
*              the right of the cursor is not moved or disturbed.
*
*              This is great when you want to input text in a certain
*              on-screen area.
*
*              E.g. display "Enter ID: [    ] (4 chars max)" and move
*              the cursor back to here  ^ and then call GetLine
*              and the "] (4 chars max)" will not be moved.
*
*              You must specify a maxlen for this to work correctly.
*
*          GL_CVTUPPER - This converts all characters to upper case
*              when they are pressed.
*
*          GL_NOINACTIVITY - This disables inactivity checking for
*               this call to GetLine.  Primarily for use with
*               AwaitConnect doors.
*
*          GL_COUNTDOWN - This function displays a countdown timer
*               that ticks down.  When the user presses a key the
*               countdown is removed and stopped.
*
*               You must specify a value for Timeout.
*
*               Currently the countdown appears as a number between
*               two square braces, e.g. "[10]"
*
*          GL_CURSORRETURN - This makes DOOR_GetLine() return
*               if the user presses cursor up or cursor down on their
*               keyboard.  DOOR_GetLine() will then return
*               either IN_CURSORUP or IN_CURSORDOWN.
*               This is so that you can make a cursor/the focus move
*               in your programs (kind like tab cycling in a gui..)
*               Do NOT specify GL_HISTORY when using this flag.
*
*      PasswordChar - a character to be used instead of the keys that
*          the user presses when GL_DISPLAY is used.
*          Set this to '\0' if you want to have the keys displayed
*          as the user presses them.
*          This option is normally used when asking the user to
*          input a password.
*
*      MaxLen - This specifies the maximum amount of characters that
*          the user is allowed to input, it must NOT be longer than
*          LEN_CURRENTLINE.
*
*      Timeout - Specify the timeout (in seconds)
*          See GL_COUNTDOWN above!
*
*      PromptStr - This is copied to NodeData->CurrentLine and is
*          displayed to the user, and the cursor will be positioned
*          after the last character.
*
*   RESULT
*      The actual string that the user entered will be stored
*      as a null terminated string in NodeData->CurrentLine, which
*      you should treat as read-only.
*
*      Most of the return codes are only of use for await and chat
*      doors.
*
*      Normally, you can ignore the result of this function, but it
*      is *critical* that you check NodeData->OnlineStatus to see
*      if the user lost carrier after *every* call to GetLine.
*      If the user has lost carrier your door must not output any
*      more text to the serial device! (otherwise you'll be sending
*      commands to the modem!)  And you may end up with a hung node
*      if you attempt to ask the user for more information!
*
*      This function will return the following numbers depending on
*      what door is running and what happened while this function was
*      active.
*
*      IN_NOTHING (0) - Reserved use.
*
*      IN_GOTLINE (1) - Returned if function actually got a line of
*          text from somewhere.
*
*      IN_LOSSCARRIER (2) - Returned only if NodeData->Logintype !=
*          LOGIN_NONE, or if the sysop logged (kicked) the user off
*
*      IN_LOGIN (3) - Returned if N_ND->LoginType==LOGIN_NONE and
*          sysop wants to local login
*
*      IN_SHUTDOWN (4) - Will only ever be returned to the
*          AwaitConnect door.  This is due the the fact that
*          NodeData->RequestShutdown can only be set if there's no
*          user online and therefore no door can be running!
*
*      IN_IMMEDIATE (5) - This is returned if the user entered some
*          text that reached MaxLen without the user having pressed
*          return.
*
*      IN_TERMINAL (6) - This is returned if the user pressed the
*          key that runs the terminal door from the AwaitConnect
*          door.
*
*      IN_TIMEOUT (7) - A timeout occured before the user could
*          enter a whole line of text (not always by Timeout, it
*          could also be because the user ran out of time or
*          an inactivity disconnect)
*
*      IN_ENDCHAT (8) - Returned if the chat door is running and
*          the sysop presses the chat start key again.
*
*      IN_DISPLAYAWAIT (9) - Returned if the sysop presses F4 at Await
*          connect prompt.
*
*      IN_DIALOUT (10) - If the sysop presses the key to dial a phone
*          number this will be returned, (only from the AwaitConnect
*          door)
*
*      IN_PAGEANSWERED (11) - If a SYSOPCHAT door is NOT running when
*          the sysop enters chat mode this will be returned.  This is
*          useful for pager doors.
*          Control will be returned to this function when the sysop
*          has FINISHED chatting to the user.
*
*
*   SEE ALSO
*      HBBSCommon.library/RemoveSpaces()
*
*********************************************************************/

ULONG __asm __saveds LIBDOOR_GetLine(register __d0 ULONG Flags, register __d1 char PasswordChar,register __d2 ULONG MaxLen,register __d3 ULONG Timeout,register __a0 UBYTE *PromptStr)
{
  char c=PasswordChar;

  return(SendDoorIOMessage(DOORIO_GETLINE,&c,1,Flags,PromptStr,MaxLen,Timeout));
}

/****** HBBSNode.library/DOOR_UpdateNodeStatus ***********************
*
*   NAME
*      DOOR_UpdateNodeStatus - Update's a part of the node's status
*          line.
*
*   SYNOPSIS
*      DOOR_UpdateNodeStatus( What )
*                             D0
*
*      void DOOR_UpdateNodeStatus( ULONG *What )
*
*   FUNCTION
*      This function is used to update the status information of the
*      node's status window.
*
*   INPUTS
*      What - Flag, can be one of:
*          You can only specify one at a time (this change in future!)
*          UPD_NAME - Updates the name/handle
*          UPD_GROUP - Updates the user's group part
*          UPD_ACTION - Updates the action part
*          UPD_CPSBAUD - Updates the current CPS or BAUD rate
*              depending on the value of NodeData->TransferringFile
*
*   RESULT
*      None
*
*   SEE ALSO
*      None
*
*********************************************************************/

void __asm __saveds LIBDOOR_UpdateNodeStatus(register __d0 ULONG What)
{
  UBYTE *str=NULL;
  UWORD X=0,MaxLen;
  switch(What)
  {
    case UPD_NAME:
      if (N_ND->User->Valid) str=N_ND->User->NormalData->Handle;
      X=4;
      MaxLen=MAX_NAME_LEN;
      break;
    case UPD_GROUP:
      if (N_ND->User->Valid) str=N_ND->User->NormalData->Group;
      X=164;
      MaxLen=MAX_GROUP_LEN;
      break;
    case UPD_ACTION:
      str=N_ND->Action;
      X=324;
      MaxLen=MAX_ACTION_LEN;
      break;
    case UPD_CPSBAUD:
      if (N_ND->TransferringFile)
      {
        str=N_ND->LastCPS;
      }
      else
      {
        str=N_ND->ConnectBaud; // *C* add cps here
      }
      X=556;
      MaxLen=MAX_CPSBAUD_LEN;
      break;
    default:
      return;
  }
  // ok, have we got an option ?
  // and a string to print ?
  // AND if the window open ?
  if ((X) && (str) && (!N_ND->NodeSettings.Iconified))
  {
    SetAPen(N_ND->NodeWnd->RPort,BBSGlobal->ScreenInfo->ScrPens[BACKGROUNDPEN]);
    RectFill(N_ND->NodeWnd->RPort, X+N_ND->NodeWnd->BorderLeft,15+N_ND->NodeWnd->BorderTop,X+N_ND->NodeWnd->BorderLeft+(8*MaxLen),15+N_ND->NodeWnd->BorderTop+9);

    SetBPen(N_ND->NodeWnd->RPort,BBSGlobal->ScreenInfo->ScrPens[BACKGROUNDPEN]);

    if ((What == UPD_NAME) && (N_ND->NodeFlags & NFLG_PAGED))
    {
      SetAPen(N_ND->NodeWnd->RPort,BBSGlobal->ScreenInfo->ScrPens[HIGHLIGHTTEXTPEN]); // white!
    }
    else
    {
      SetAPen(N_ND->NodeWnd->RPort,BBSGlobal->ScreenInfo->ScrPens[TEXTPEN]); // black!
    }
    Move(N_ND->NodeWnd->RPort,1+X+N_ND->NodeWnd->BorderLeft,8+14+N_ND->NodeWnd->BorderTop);  // 8 for text height, 13 for placement
    Text(N_ND->NodeWnd->RPort,str,(strlen(str)>MaxLen) ? MaxLen : strlen(str));
  }
}

/****** HBBSNode.library/DOOR_SystemDoor *****************************
*
*   NAME
*      DOOR_SystemDoor - Calls a system door.
*
*   SYNOPSIS
*      DOOR_SystemDoor( doorname, options )
*                       A0        D0
*
*      void DOOR_SystemDoor( UBYTE *doorname, UBYTE *options )
*
*   FUNCTION
*      This function calls another door that has been defined in
*      a Commands/System file, e.g. "HBBS:Commands/System".
*
*      Control is NOT returned until the called door has returned.
*
*   INPUTS
*      doorname - a null terminated string containing the doorname
*      options - a null terminated string containing paramaters that
*      are passed to the door as command line arguments.
*
*      e.g.  To tag a file you might do this
*
*      sprintf(myparams,"C=%d,N=%s",confnum,filename);
*      DOOR_SystemDoor("Tag_File",myparams);
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_Return(), DOOR_UserDoor(), DOOR_Continue()
*
*********************************************************************/

void __asm __saveds LIBDOOR_SystemDoor(register __a0 UBYTE *doorname,register __a1 UBYTE *options)
{
  SendDoorIOMessage(DOORIO_SYSTEMDOOR,doorname,0,0,options,0,0);
}

/****** HBBSNode.library/DOOR_UserDoor *******************************
*
*   NAME
*      DOOR_UserDoor - Calls a system door.
*
*   SYNOPSIS
*      DOOR_UserDoor( doorname, options )
*                       A0        D0
*
*      void DOOR_UserDoor( UBYTE *doorname, UBYTE *options )
*
*   FUNCTION
*      This function calls another door that has been defined in
*      a Commands/Level#? file, e.g. "HBBS:Commands/Level_50".
*
*      Control is NOT returned until the called door has returned.
*
*   INPUTS
*      doorname - a null terminated string containing the doorname
*      options - a null terminated string containing paramaters that
*      are passed to the door as command line arguments.
*
*      e.g.  To run the config of the mailscanner you might do this:
*
*      DOOR_UserDoor("MS","?");
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_Return(), DOOR_SystemDoor(), DOOR_Continue()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_UserDoor(register __a0 UBYTE *doorname,register __a1 UBYTE *options)
{
  return(SendDoorIOMessage(DOORIO_USERDOOR,doorname,0,0,options,0,0));
}

/****** HBBSNode.library/DOOR_HangUp *********************************
*
*   NAME
*      DOOR_HangUp - Hang up the modem.
*
*   SYNOPSIS
*      DOOR_HangUp( void )
*
*      void DOOR_HangUp( void )
*
*   FUNCTION
*      This function hangs up the modem
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_GoodBye()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_HangUp( void )
{
  return(SendDoorIOMessage(DOORIO_HANGUP,NULL,0,0,NULL,0,0));
}

/****** HBBSNode.library/DOOR_Return *********************************
*
*   NAME
*      DOOR_Return - Return a string.
*
*   SYNOPSIS
*      DOOR_Return( returnstring )
*                   A0
*
*      void DOOR_Return( UBYTE *returnstring )
*
*   FUNCTION
*      This function is used to return a string to the program/door
*      that called the current door.  This lets you pass messages
*      back to the calling program/door.
*
*      e.g. say the CheckFiles door called the dupechecker, and
*      the dupechecker detected a duplicate file, it might do this:
*
*      DOOR_Return("DUPLICATE");
*
*      Then the CheckFiles door would check NodeData->DoorReturn
*      the to see if contained the string "DUPLICATE"
*      e.g: if (iposition("DUPLICATE",N_ND->DoorReturn) >= 0 ) ...
*
*   INPUTS
*      a string to return, not longer than LEN_MAXDOORRETURN
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_Continue()
*
*********************************************************************/

void __asm __saveds LIBDOOR_Return( register __a0 UBYTE *returnstring )
{
  strNcpy(N_ND->DoorReturn,returnstring,LEN_MAXDOORRETURN);
}

/****** HBBSNode.library/DOOR_DisplayScreen **************************
*
*   NAME
*      DOOR_DisplayScreen - Display a screen/text file.
*
*   SYNOPSIS
*      DOOR_DisplayScreen( screenname )
*                          A0
*
*      V_BOOL DOOR_DisplayScreen( UBYTE *screenname )
*
*   FUNCTION
*      This function opens the screenname specified and displays the
*      contents of the file, if the file starts with "@^@" then all
*      other @^@ codes will be processed
*
*      After displaying a screen it is good practise to check if the
*      node is still on-line.  The user may have lost carrier while
*      the screen was being displayed, or another door may have been
*      called by a @^@ code which hung up the modem, etc.
*
*      So check NodeStatus->OnlineStatus after each call.
*
*   INPUTS
*      screenname - null terminated absolute filename.
*          The filename must be an absolute filename because the
*          device/assign/volume it resides on is checked, to avoid
*          "Please Insert Volume xxx:" requesters.
*          So, "HBBS:Screens/Misc/myscreen.txt" is valid, but
*          "Screens/Misc/myscreen.txt" is not.
*
*   RESULT
*      TRUE if a screen was displayed.
*
*   SEE ALSO
*      DOOR_DisplaySpecialScreen(), HBBS:Docs/Reference/Screens.Guide
*      BBSGlobal/LanguageName_XX, BBSGlobal/LanguageExtn_XX
*      DOOR_DisplyBulletin()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_DisplayScreen( register __a0 UBYTE *screenname )
{
  return(SendDoorIOMessage(DOORIO_DISPLAYSCREEN,screenname,0,0,NULL,0,0));
}

/****** HBBSNode.library/DOOR_DisplaySpecialScreen *******************
*
*   NAME
*      DOOR_DisplaySpecialScreen - Display a special screen.
*
*   SYNOPSIS
*      DOOR_DisplaySpecialScreen( screenname )
*                                 A0
*
*      V_BOOL DOOR_DisplaySpecialScreen( UBYTE *screenname )
*
*   FUNCTION
*      This function searches for a screen in the manor described
*      in HBBS:Docs/Reference/Screens.Guide, adding the user's
*      selected language extension to the filename as defined by
*      BBSGlobal/LanguageName_XX and BBSGlobal/LanguageExtn_XX.
*
*      If a screen file is found, the filename is then passed to
*      DOOR_DisplayScreen(), see that for further information.
*
*      Only the first screen file that is found will be displayed.
*
*      After displaying a screen it is good practise to check if the
*      node is still on-line.  The user may have lost carrier while
*      the screen was being displayed, or another door may have been
*      called by a @^@ code which hung up the modem, etc.
*
*      So check NodeStatus->OnlineStatus after each call.
*
*   INPUTS
*      screenname - null terminated screen name.
*
*   RESULT
*      TRUE if a screen was displayed.
*
*   SEE ALSO
*      DOOR_DisplayScreen(), HBBS:Docs/Reference/Screens.Guide
*      BBSGlobal/LanguageName_XX, BBSGlobal/LanguageExtn_XX
*      DOOR_DisplayBulletin()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_DisplaySpecialScreen( register __a0 UBYTE *screenname )
{
  return(SendDoorIOMessage(DOORIO_DISPLAYSPECIALSCREEN,screenname,0,0,NULL,0,0));
}

/****** HBBSNode.library/DOOR_PausePrompt ****************************
*
*   NAME
*      DOOR_PausePrompt - Display a pause prompt.
*
*   SYNOPSIS
*      DOOR_PausePrompt( prompt )
*                        A0
*
*      void DOOR_PausePrompt( UBYTE *prompt )
*
*   FUNCTION
*      This function displays the prompt to the user, and waits
*      for the a key to be pressed.
*
*   INPUTS
*      prompt - a null-terminated string, or NULL for the default
*         prompt, which differs depending on the conference and node.
*         If a null terminated string is passed, it is first copied
*         and then passed through HBBS_ModifyString() so that
*         it is colourised and string replacements are made.
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_ModifyString(), DOOR_ContinuePrompt(),
*      DOOR_MenuPrompt(), DOOR_AContinuePrompt()
*
*********************************************************************/

void __asm __saveds LIBDOOR_PausePrompt( register __a0 UBYTE *prompt )
{
  QuickDoorIOMessage(DOORIO_PAUSEPROMPT,prompt);
}

void __asm __saveds LIBDOOR_Add_Last_Upload( register __a0 UBYTE *details )
{
  QuickDoorIOMessage(DOORIO_ADDLASTUPLOAD,details);
}

void __asm __saveds LIBDOOR_Add_Last_Download( register __a0 UBYTE *details )
{
  QuickDoorIOMessage(DOORIO_ADDLASTDOWNLOAD,details);
}

/****** HBBSNode.library/DOOR_ContinuePrompt *************************
*
*   NAME
*      DOOR_ContinuePrompt - Display a continue prompt.
*
*   SYNOPSIS
*      DOOR_ContinuePrompt( prompt, Flags )
*                           A0      D0
*
*      void DOOR_ContinuePrompt( UBYTE *prompt, V_BIGNUM Flags )
*
*   FUNCTION
*      This function displays the prompt to the user, and waits
*      for either Y or N or return to be pressed.
*
*   INPUTS
*      prompt - a null-terminated string to display, or NULL for the
*         default prompt, which can differ depending on the conference
*         and node.
*
*      Flags - to be logically OR'd together.
*
*          DCP_NONE - accept internal defaults (subject to change at)
*          DCP_DEFAULT_YES - set the default to YES
*          DCP_DEFAULT_NO - set the default to NO
*          DCP_ADDYN - add's [Y/n] or [y/N] accordingly.
*          DCP_CLEAR - removes the prompt line after continuing.
*          DCP_NODISPLAY - does not display Y or N when the user
*              presses a key.
*          DCP_COLOURIZE - passes the prompt string through
*              DOOR_MenuPrompt() before displaying it
*
*
*   RESULT
*      TRUE if the user presses Y, or FALSE if the user presses N
*
*      If the user looses carrier FALSE is returned
*
*   NOTES
*      Specifing the DCP_TIMEOUT flag, from DOOR_AContinuePrompt()
*      as one of the flags here will have no effect, but don't do
*      it anyway!
*
*
*   SEE ALSO
*      HBBS_ModifyString(), DOOR_PausePrompt(), DOOR_AContinuePrompt()
*      DOOR_MenuPrompt()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_ContinuePrompt( register __a0 UBYTE *prompt,register __d0 V_BIGNUM Flags )
{
  return(SendDoorIOMessage(DOORIO_CONTINUEPROMPT,prompt,0,Flags,NULL,0,0));
}

/****** HBBSNode.library/DOOR_AContinuePrompt ************************
*
*   NAME
*      DOOR_AContinuePrompt - Display a continue prompt.
*
*   SYNOPSIS
*      DOOR_AContinuePrompt( prompt, Flags, Timeout )
*                            A0      D0     D1
*
*      void DOOR_AContinuePrompt( UBYTE *prompt, V_BIGNUM Flags,
*         ULONG Timeout )
*
*   FUNCTION
*      This function displays the prompt to the user, and waits
*      for either Y or N or return to be pressed.
*      It is the same is DOOR_ContinuePrompt, except that it can
*      accept an extra flag and a timeout (used by the extra flag)
*
*   INPUTS
*      prompt - see DOOR_ContinuePrompt()
*
*      Flags - to be logically OR'd together.
*
*          DCP_COUNTDOWN - adds a countdown timer
*          DCP_... see DOOR_ContinuePrompt()
*
*   RESULT
*      TRUE if the user presses Y, or FALSE if the user presses N
*
*      If the user looses carrier FALSE is returned
*
*   SEE ALSO
*      HBBS_ModifyString(), DOOR_ContinuePrompt(), DOOR_PausePrompt()
*      DOOR_MenuPrompt()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_AContinuePrompt( register __a0 UBYTE *prompt,register __d0 V_BIGNUM Flags,register __d1 ULONG Timeout)
{
  return(SendDoorIOMessage(DOORIO_ACONTINUEPROMPT,prompt,0,Flags,NULL,Timeout,0));
}

/****** HBBSNode.library/DOOR_MenuPrompt *****************************
*
*   NAME
*      DOOR_MenuPrompt - Display a prompt, for a menu.
*
*   SYNOPSIS
*      DOOR_MenuPrompt( promptstr, promptdefault )
*                       A0         D0
*
*      void DOOR_MenuPrompt( char *promptstr, char promptdefault )
*
*   FUNCTION
*      This function displays a prompt, with options in colour and the
*      default option highlighted.
*
*      This function does not actually wait for the user to press
*      any keys, so a call to this function is normally followed by
*      a call to DOOR_GetLine()
*
*   INPUTS
*      promptstr - The menu prompt, if this string contain's square
*          braces ('[' or ']') then they will be replaced with the
*          strings defines in the current BBSColours.CFG file.
*          (which may contain ANSI and may remove or change the
*          square braces completly).
*          If the string ends with a ':' character then it will
*          be replaced with the default ANSI prompt string.
*      promptdefault - If this character is found between two square
*          braces then it will be highlighted in some way (depending
*          (again) on the configuration of the BBSColours.CFG file)
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_GetLine(), DOOR_PausePrompt(), DOOR_ContinuePrompt()
*      DOOR_AContinuePrompt()
*      HBBS:System/Data/BBSColours.CFG
*
*********************************************************************/

void __asm __saveds LIBDOOR_MenuPrompt(register __a0 char *promptstr,register __d0 char promptdefault)
{
  char options[2];
  options[0]=promptdefault;
  options[1]=0;

  SendDoorIOMessage(DOORIO_MENUPROMPT,promptstr,0,0,options,0,0);
}

/****** HBBSNode.library/DOOR_Continue *******************************
*
*   NAME
*      DOOR_Continue - Allows other doors with the same name to run
*
*   SYNOPSIS
*      DOOR_Continue( Continue )
*                     D0
*
*      void DOOR_Continue( V_BOOL Continue )
*
*   FUNCTION
*      This function lets the current door decide if other doors that
*      have the same name run when this one finishes.
*
*      this would be usefull if your door could not complete it's task
*      and another door depends on it.
*
*      E.g.  say you had a commands/system that looked like this
*
*      MyDoor_Type_1=HBBS
*      MyDoor_Door_1=<door 1>
*      MyDoor_Type_2=HBBS
*      MyDoor_Door_2=<door 2>
*
*      And <door 2> depends on <door 1>'s sucessful completion you
*      would call DOOR_Continue( FALSE ) and <door 2> would not be
*      run.
*
*   INPUTS
*      Continue - boolean allow or dis-allow other doors to run
*          after the door calling this function, that have the same
*          name.
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_Return()
*
*********************************************************************/

void __asm __saveds LIBDOOR_Continue( register __d0 V_BOOL Continue )
{
  N_ND->DoorContinue=Continue;
}

/****** HBBSNode.library/ValidConfNum ********************************
*
*   NAME
*      ValidConfNum - Find the user's conference.
*
*   SYNOPSIS
*      ValidConfNum( ConfNum )
*
*      V_BOOL ValidConfNum( ConfNum )
*
*   FUNCTION
*      This function checks to see if a conference exists.
*
*   INPUTS
*      ConfNum - number of a conference, from 1 to
*          BBSGlobal->Conferences
*
*   RESULT
*      TRUE if the conference exists.
*
*   NOTES
*      A user may or may not have access to the specified conference
*
*   SEE ALSO
*      HBBS_AllowConfAccess(), FindConf(), CheckUser system door
*
*********************************************************************/

V_BOOL __asm __saveds LIBValidConfNum(register __d0 V_BIGNUM ConfNum)
{
  // index from 1 (i.e. first conf = 1)
  return((V_BOOL)((ConfNum>0 && ConfNum <=BBSGlobal->Conferences) ? TRUE : FALSE));
}

/****** HBBSNode.library/FindConf ************************************
*
*   NAME
*      FindConf - Find the user's conference.
*
*   SYNOPSIS
*      FindConf( void )
*
*      struct ConfData *FindConf( void )
*
*   FUNCTION
*      This function finds the user's current conference, or, if
*      they haven't joined a conference yet, the conference that they
*      were last in.
*
*      This function should be used with care.  Some of though
*      must be paid to what might happen if you use/store this
*      information, or if you pass it to other doors which depend
*      on the user *actually* being in a conference.
*
*      It can also return a NULL pointer, so care must be taken
*      to check the result.  It can only return NULL if the user's
*      last conference does not exist.
*
*      Also, the conference may be validm but the user may not be
*      able to see or even access the conference.
*
*      Also bear in mind, that the user's last conference may be
*      have a different number now, if the sysop has re-organized
*      the conferences.
*
*   INPUTS
*      None
*
*   RESULT
*      struct ConfData * - a pointer to a conference data structure,
*        or NULL if the user's last conference does not exist.
*
*   SEE ALSO
*      HBBS_AllowConfAccess(), CheckUser system door
*
*********************************************************************/

struct ConfData __asm __saveds *LIBFindConf( void )
{
  struct ConfData *Conf=NULL;
  V_BIGNUM ConfNum;


  if (N_ND->CurrentConf)
  {
    Conf=N_ND->CurrentConf;
  }
  else
  {
    if (N_ND->User->Valid)
    {
      if (ConfNum=N_ND->User->CallData->LastConf)
      {
        if (LIBValidConfNum(ConfNum))
        {
          Conf=(struct ConfData *)GetNode(BBSGlobal->ConfList,ConfNum-1);
        }
      }
    }
  }
  return(Conf);

}


void  __asm __saveds LIBLoadAccess( register __a0 char *filename, register __a1 struct AccessData *AD)
{
  struct CfgFileData *CfgFile;
  V_BOOL acsdata;

  if (CfgFile=HBBS_LoadConfig(filename,LCFG_NONE))
  {

    /* Main Options */

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_DLFILES,OPT_SINGLE))
      AD->Data[ACS_DLFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ULFILES,OPT_SINGLE))
      AD->Data[ACS_ULFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_SYSOPCOMMENT,OPT_SINGLE))
      AD->Data[ACS_SYSOPCOMMENT]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_READMAIL,OPT_SINGLE))
      AD->Data[ACS_READMAIL]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_WRITEMAIL,OPT_SINGLE))
      AD->Data[ACS_WRITEMAIL]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_SCANMAIL,OPT_SINGLE))
      AD->Data[ACS_SCANMAIL]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWSTATUS,OPT_SINGLE))
      AD->Data[ACS_ALLOWSTATUS]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWEDIT,OPT_SINGLE))
      AD->Data[ACS_ALLOWEDIT]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_SCANFILES,OPT_SINGLE))
      AD->Data[ACS_SCANFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWVIEW,OPT_SINGLE))
      AD->Data[ACS_ALLOWVIEW]=acsdata ? 'Y' : 'N';


    /* Mail Options */

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_DELETEMAIL,OPT_SINGLE))
      AD->Data[ACS_DELETEMAIL]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_DELETEGROUP,OPT_SINGLE))
      AD->Data[ACS_DELETEGROUP]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_DELETEANYONE,OPT_SINGLE))
      AD->Data[ACS_DELETEANYONE]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWALL,OPT_SINGLE))
      AD->Data[ACS_ALLOWALL]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWEVERYBODY,OPT_SINGLE))
      AD->Data[ACS_ALLOWEVERYBODY]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWMULTIPLE,OPT_SINGLE))
      AD->Data[ACS_ALLOWMULTIPLE]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ATTACHFILES,OPT_SINGLE))
      AD->Data[ACS_ATTACHFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWMULTIFILES,OPT_SINGLE))
      AD->Data[ACS_ALLOWMULTIFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWPRIVATEFILES,OPT_SINGLE))
      AD->Data[ACS_ALLOWPRIVATEFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWALLFILES,OPT_SINGLE))
      AD->Data[ACS_ALLOWALLFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_ALLOWEVERYONEFILES,OPT_SINGLE))
      AD->Data[ACS_ALLOWEVERYONEFILES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_AUTOEXPIREMSG,OPT_SINGLE))
      AD->Data[ACS_AUTOEXPIREMSG]=acsdata ? 'Y' : 'N';

    /* W command settings */

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITHANDLE,OPT_SINGLE))
      AD->Data[ACS_EDITHANDLE]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITREALNAME,OPT_SINGLE))
      AD->Data[ACS_EDITREALNAME]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITPASSWORD,OPT_SINGLE))
      AD->Data[ACS_EDITPASSWORD]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITGROUP,OPT_SINGLE))
      AD->Data[ACS_EDITGROUP]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITLOCATION,OPT_SINGLE))
      AD->Data[ACS_EDITLOCATION]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITCOUNTRY,OPT_SINGLE))
      AD->Data[ACS_EDITCOUNTRY]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITCOMPUTER,OPT_SINGLE))
      AD->Data[ACS_EDITCOMPUTER]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITPHONENUMBER,OPT_SINGLE))
      AD->Data[ACS_EDITPHONENUMBER]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITSCREENTYPE,OPT_SINGLE))
      AD->Data[ACS_EDITSCREENTYPE]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITLINES,OPT_SINGLE))
      AD->Data[ACS_EDITLINES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITEDITOR,OPT_SINGLE))
      AD->Data[ACS_EDITEDITOR]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_EDITPROTOCOL,OPT_SINGLE))
      AD->Data[ACS_EDITPROTOCOL]=acsdata ? 'Y' : 'N';

    /* ULIMITEDxxxx Settings */

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_UNLIMTIME,OPT_SINGLE))
      AD->Data[ACS_UNLIMTIME]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_UNLIMCALLS,OPT_SINGLE))
      AD->Data[ACS_UNLIMCALLS]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_UNLIMBYTES,OPT_SINGLE))
      AD->Data[ACS_UNLIMBYTES]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_UNLIMCHAT,OPT_SINGLE))
      AD->Data[ACS_UNLIMCHAT]=acsdata ? 'Y' : 'N';



/*  Spare ones


    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_,OPT_SINGLE))
      AD->Data[ACS_]=acsdata ? 'Y' : 'N';

    if (HBBS_GetSetting(CfgFile,(void *)&acsdata,VTYPE_BOOL,ACSSTR_,OPT_SINGLE))
      AD->Data[ACS_]=acsdata ? 'Y' : 'N';
*/
    HBBS_FlushConfig(CfgFile);
  }

}

/****** HBBSNode.library/HBBS_ModifyString ***************************
*
*   NAME
*      HBBS_ModifyString - Modify a string.
*
*   SYNOPSIS
*      HBBS_ModifyString( str )
*                         A0
*
*      char *HBBS_ModifyString( char *str )
*
*   FUNCTION
*      This function takes a string and modifies it, replacing items
*      enclosed in curly braces ('{' and '}') with data.
*
*      This is great for making menu prompts and such like.
*
*   INPUTS
*      str - a null terminated string. See NOTES below for important
*          information regarding the size!
*
*          If str contains any of the following items, they will
*          be replaced accordingly.
*
*          {L} - Time left on-line (0 if user has ACS_UNLIMITEDTIME)
*          {O} - Time user has been on-line
*          {S} - The BBS's name (taken from BBSGlobalData->BBSName)
*          {C} - The name of the current conference, if a user is not
*                in a conference then this will be left blank.
*          {T} - Actual time, without seconds
*          {TS} - Actual time, with seconds
*          {D} - Actual date. (DD-MM-YYYY)
*          {B} - Bytes left in current conference
*                (Not implemented yet!)
*          {E} - ANSI control sequence introducer.
*          {H} - The user's handle, if the user is not valid at the
*                time this is called, then this will be left blank
*          {G} - The user's group, if the user is not valid at the
*                time this is called, then this will be left blank
*
*   NOTES
*      No memory size checking is done, so your string must be big
*      enough to handle all the replacements.
*      So make sure you allocate a big string!
*
*   RESULT
*      returns a pointer to the input string (str)
*
*   SEE ALSO
*      HBBS_AllowConfAccess(), CheckUser system door
*      HBBS:Docs/Configuration/ConfConfig.guide/MenuPrompt
*
*********************************************************************/

char __asm __saveds *LIBHBBS_ModifyString(register __a0 char *str)
{
  char tmpstr[20];

  // *D* Document the {x} codes

  // Time Left

  sprintf(tmpstr,"%d",LIBHBBS_TimeLeft());
  replace(str,str,"{L}",tmpstr);

  // Time Online

  sprintf(tmpstr,"%d",LIBHBBS_TimeOnline());
  replace(str,str,"{O}",tmpstr);

  // System Name

  replace(str,str,"{S}",BBSGlobal->BBSName);

  // Conference Name

  if (N_ND->CurrentConf)
    replace(str,str,"{C}",N_ND->CurrentConf->node.ln_Name);
  else
    replace(str,str,"{C}","");

  // Actual Time Without Seconds

  HBBS_GetTime(tmpstr);
  tmpstr[5]=0;
  replace(str,str,"{T}",tmpstr);

  // Actual Time WITH seconds

  HBBS_GetTime(tmpstr);
  replace(str,str,"{TS}",tmpstr);

  // Actual Date

  HBBS_GetDate(tmpstr);
  replace(str,str,"{D}",tmpstr);

  // Credits Left (In Current Conference)

  replace(str,str,"{B}","<Creds>"); // *C* IMPLEMENT!

  // Ansi CSI

  replace(str,str,"{E}","\033[");


  if (N_ND->User->Valid)
  {
    replace(str,str,"{H}",N_ND->User->CallData->Handle); // Users Handle
    replace(str,str,"{G}",N_ND->User->CallData->Group); // Users Group
  }
  else
  {
    replace(str,str,"{H}","");
    replace(str,str,"{G}","");
  }

  return(str);
}

V_BOOL __asm __saveds LIBHBBS_LoadConfAcs( register __a0 struct ConfAcsData *ConfAcs, register __a1 char *filename)
{
  V_BOOL retval=FALSE,error=FALSE;

  struct CfgFileData *CfgFile;
  int loop;
  struct BoolNode *tmpnode;
  V_BOOL TmpVal;
  char tmpstr[BIG_STR];

  if (CfgFile=HBBS_LoadConfig(filename,LCFG_NONE))
  {
    if (HBBS_GetSetting(CfgFile,(void *)&ConfAcs->Name,VTYPE_STRING,"Name",OPT_SINGLE))
    {
      TmpVal=FALSE;
      HBBS_GetSetting(CfgFile,(void *)&TmpVal,VTYPE_BOOL,"Default",OPT_SINGLE);

      // set allow/disallow conf access to whatever "default" is set to for all conferences
      for (loop=0;!error && loop<BBSGlobal->Conferences;loop++)
      {
        tmpnode=(struct BoolNode*)GetNode(ConfAcs->Access,loop);
        tmpnode->Boolean=TmpVal;
      }

      // then make this value the opposite for all "Override_<confnum>=YES"'s
      for (loop=0;!error && loop<BBSGlobal->Conferences;loop++)
      {
        sprintf(tmpstr,"Override_%d",loop+1);
        if (HBBS_GetSetting(CfgFile,(void *)&TmpVal,VTYPE_BOOL,tmpstr,OPT_SINGLE) && TmpVal==TRUE)
        {
          tmpnode=(struct BoolNode*)GetNode(ConfAcs->Access,loop);
          tmpnode->Boolean=!tmpnode->Boolean;
        }
      }

      // then do the same thing for SeeDefault and all See_<confnum>'s
      TmpVal=FALSE;
      HBBS_GetSetting(CfgFile,(void *)&TmpVal,VTYPE_BOOL,"SeeDefault",OPT_SINGLE);

      for (loop=0;!error && loop<BBSGlobal->Conferences;loop++)
      {
        tmpnode=(struct BoolNode*)GetNode(ConfAcs->See,loop);
        tmpnode->Boolean=TmpVal;
      }

      for (loop=0;!error && loop<BBSGlobal->Conferences;loop++)
      {
        sprintf(tmpstr,"SeeOverride_%d",loop+1);
        if (HBBS_GetSetting(CfgFile,(void *)&TmpVal,VTYPE_BOOL,tmpstr,OPT_SINGLE) && TmpVal==TRUE)
        {
          tmpnode=(struct BoolNode*)GetNode(ConfAcs->See,loop);
          tmpnode->Boolean=!tmpnode->Boolean;
        }
      }
      retval=TRUE;
    }
    HBBS_FlushConfig(CfgFile);
  }
  return(retval);
}

/****** HBBSNode.library/HBBS_SetAccess *****************************
*
*   NAME
*      HBBS_SetAccess - Force a re-load of the access level settings
*
*   SYNOPSIS
*      HBBS_SetAccess( void )
*
*      void HBBS_SetAccess( void )
*
*   FUNCTION
*      This function will force the node to re-load the access level
*      settings from the current conference or node.
*
*      This is normally only used when changing conference or access
*      level, so it should really only be called by the AccountEdit
*      or JoinConference door.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_SetBBSStrings(), HBBS_SetBBSCols()
*
*********************************************************************/

void __asm __saveds LIBHBBS_SetAccess( void )
{
  char tmpfilename[BIG_STR];
  V_BIGNUM loop;

  // Clear out the access

  for (loop=0;loop<MAX_ACCESSSETTINGS;loop++)
  {
    N_ND->User->Acs.Data[loop]='N';
  }

  if (N_ND->User->Valid)
  {
    N_ND->User->Acs.AccessLevel=N_ND->User->CallData->Access;

    LIBLoadAccess("HBBS:Access/Levels/Level_Global",&N_ND->User->Acs);

    sprintf(tmpfilename,"HBBS:Access/Levels/Level_%d",N_ND->User->Acs.AccessLevel);
    LIBLoadAccess(tmpfilename,&N_ND->User->Acs);

    sprintf(tmpfilename,"%sAccess/Level_%d",N_ND->NodeLocation,N_ND->User->Acs.AccessLevel);
    LIBLoadAccess(tmpfilename,&N_ND->User->Acs);

    if (N_ND->CurrentConf)
    {
      sprintf(tmpfilename,"%sAccess/Level_%d",N_ND->CurrentConf->ConfPath,N_ND->User->Acs.AccessLevel);
      LIBLoadAccess(tmpfilename,&N_ND->User->Acs);
    }

    sprintf(tmpfilename,"HBBS:Access/Users/%s",N_ND->User->CallData->Handle);
    LIBLoadAccess(tmpfilename,&N_ND->User->Acs);

    sprintf(tmpfilename,"HBBS:System/Data/ConfAcs/%s.CFG",N_ND->User->CallData->ConfAcsDataFile);
    if (!LIBHBBS_LoadConfAcs(&N_ND->User->ConfAcs,tmpfilename))
    {
      HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_ERROR_OPENING,tmpfilename,TYPE_WARNING);
    }
  }
}

/****** HBBSNode.library/HBBS_CheckAccess ****************************
*
*   NAME
*      HBBS_CheckAccess - Check an access level option.
*
*   SYNOPSIS
*      HBBS_CheckAccess( AccessOption )
*                        D0
*
*      V_BOOL HBBS_CheckAccess( ULONG AccessOption )
*
*   FUNCTION
*      Checks an access level setting for the current user.
*
*   INPUTS
*      AccessOption - an access level number to check, see
*         includes/HBBS/Access.h for a list of possible values.
*
*      e.g. to see if the current user has unlimited time, do this
*
*      HBBS_CheckAccess(ACS_UNLIMTIME)
*
*   RESULT
*      Returns TRUE if the access level is set.
*
*   SEE ALSO
*      DOOR_TimeLeft()
*
*********************************************************************/

V_BOOL __asm __saveds LIBHBBS_CheckAccess(register __d0 ULONG AccessOption)
{
  if ((N_ND->User->Valid) && (N_ND->User->Acs.Data[AccessOption]=='Y')) return(TRUE); else return(FALSE);
}

/****** HBBSNode.library/HBBS_AddToCallersLog ************************
*
*   NAME
*      HBBS_AddToCallersLog - Adds a string to the node's callers log
*
*   SYNOPSIS
*      HBBS_AddToCallersLog( String )
*                            A0
*
*      void HBBS_AddToCallersLog( UBYTE *String )
*
*   FUNCTION
*      This function adds the specified String to the current node's
*      callers log file, along with a time and date stamp.
*
*   INPUTS
*      String - null terminated plain text string, do not include ANSI
*
*   RESULT
*      None
*
*   NOTES
*      Try to make all text that that is written to the callers log
*      nicely formatted and easily read back in by log file examiners
*      Try to seperate data with keywords, e.g. "files 50 amount 35"
*      and not just "50 35"
*      Make all text/output as human readable as possible, try to make
*      problems stand out.
*
*   SEE ALSO
*      HBBSCommon.library/HBBS_LogError()
*
*********************************************************************/

void __asm __saveds LIBHBBS_AddToCallersLog(register __a0 UBYTE *String)
{
  UBYTE tmpstr[BIG_STR];

  if (N_ND->NodeSettings.CallersLogFile)
  {

    HBBS_GetDate(tmpstr);
    strcat(tmpstr," ");
    HBBS_GetTime(tmpstr+strlen(tmpstr));
    strcat(tmpstr," ");
    strcat(tmpstr,String);
    strcat(tmpstr,"\n");

    HBBS_AppendStrToFile(N_ND->NodeSettings.CallersLogFile,tmpstr);
  }
}


struct TaggedFile __asm __saveds *LIBHBBS_FindTag(register __a0 UBYTE *FileName,register __d0 BOOL ExactMatch)
{
  struct TaggedFile *Tag=NULL,*retval=NULL;

  if (N_ND->TaggedFiles)
  {
    for (Tag=(struct TaggedFile*)N_ND->TaggedFileList->lh_Head;!retval && Tag->node.ln_Succ;Tag=(struct TaggedFile *)Tag->node.ln_Succ)
    {
      if (ExactMatch)
      {
        if (stricmp(FileName,Tag->node.ln_Name)==0)
        {
          retval=Tag;
        }
      }
      else
      {
        if (stricmp(FilePart(FileName),FilePart(Tag->node.ln_Name))==0)
        {
          retval=Tag;
        }
      }
    }
  }
  return(retval);
}

/****** HBBSNode.library/HBBS_SetBBSCols *****************************
*
*   NAME
*      HBBS_SetBBSCols - Force a re-load of the BBS colours config.
*
*   SYNOPSIS
*      HBBS_SetBBSCols( void )
*
*      void HBBS_SetBBSCols( void )
*
*   FUNCTION
*      This function will force the node to re-load the BBSColours.CFG
*      file from the current conference or node.
*
*      This is normally only used when changing conference and it
*      should really only be called by the JoinConference door.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_SetBBSStrings(), HBBS_SetAccess()
*
*********************************************************************/

void __asm __saveds LIBHBBS_SetBBSCols( void )
{
  struct CfgFileData *CfgFile;
  char filename[1024],*bbscolsfile="BBSColours.CFG";

  BOOL foundfile=FALSE;
  BYTE triedall=3;

  FreeAndSet(&N_ND->BBSCols->MenuTextANSI,"[0;37m");
  FreeAndSet(&N_ND->BBSCols->MenuOpenBracket,"[0;34m[");
  FreeAndSet(&N_ND->BBSCols->MenuCloseBracket,"[0;34m]");
  FreeAndSet(&N_ND->BBSCols->MenuHighlightANSI,"[0;36m");
  FreeAndSet(&N_ND->BBSCols->MenuDefaultOptANSI,"[1;33m");
  FreeAndSet(&N_ND->BBSCols->MenuPromptANSI," [0;34m: [0;37m");
  FreeAndSet(&N_ND->BBSCols->MenuDefaultOptTextANSI,"[0;33m");

  do
  {
    triedall--;
    switch (triedall)
    {
      case 2:
        if (N_ND->CurrentConf)
        {
          strcpy(filename,N_ND->CurrentConf->ConfPath);
          strcat(filename,bbscolsfile);
        } else filename[0]=0;
        break;
      case 1:
        strcpy(filename,N_ND->NodeLocation);
        strcat(filename,bbscolsfile);
        break;
      case 0:
        strcpy(filename,"HBBS:System/Data/");
        strcat(filename,bbscolsfile);
        break;
    }
    if (filename[0])
    {
      if (CfgFile=HBBS_LoadConfig(filename,LCFG_NOSTRIPCOMMENTS|LCFG_NOSTRIPSPACES))
      {
        foundfile=TRUE;
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuTextANSI,VTYPE_STRING,"MenuTextANSI",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuOpenBracket,VTYPE_STRING,"MenuOpenBracket",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuCloseBracket,VTYPE_STRING,"MenuCloseBracket",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuHighlightANSI,VTYPE_STRING,"MenuHighlightANSI",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuDefaultOptANSI,VTYPE_STRING,"MenuDefaultOptANSI",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuPromptANSI,VTYPE_STRING,"MenuPromptANSI",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSCols->MenuDefaultOptTextANSI,VTYPE_STRING,"MenuDefaultOptTextANSI",OPT_SINGLE);

        HBBS_FlushConfig(CfgFile);

        replace(N_ND->BBSCols->MenuTextANSI,N_ND->BBSCols->MenuTextANSI,"{E}","\033");
        replace(N_ND->BBSCols->MenuOpenBracket,N_ND->BBSCols->MenuOpenBracket,"{E}","\033");
        replace(N_ND->BBSCols->MenuCloseBracket,N_ND->BBSCols->MenuCloseBracket,"{E}","\033");
        replace(N_ND->BBSCols->MenuHighlightANSI,N_ND->BBSCols->MenuHighlightANSI,"{E}","\033");
        replace(N_ND->BBSCols->MenuDefaultOptANSI,N_ND->BBSCols->MenuDefaultOptANSI,"{E}","\033");
        replace(N_ND->BBSCols->MenuPromptANSI,N_ND->BBSCols->MenuPromptANSI,"{E}","\033");
        replace(N_ND->BBSCols->MenuDefaultOptTextANSI,N_ND->BBSCols->MenuDefaultOptTextANSI,"{E}","\033");
      }
    }
  } while ((!foundfile) && (triedall>0));
}

/****** HBBSNode.library/DOOR_CheckRaw *******************************
*
*   NAME
*      DOOR_CheckRaw - Check to see if any data has come from the
*         serial device or the watch window.
*
*   SYNOPSIS
*      DOOR_CheckRaw( Flags )
*                     D0
*
*      V_BOOL DOOR_CheckRaw( ULONG Flags )
*
*   FUNCTION
*      This function checks and retrieves raw bytes from the serial
*      device and console device for the node's watch window, it is
*      primarily used to enable doors to do ctrl-c checking.
*      While performing functions that take quite a long time, eg.
*      MailScan or FileList searching.
*
*   INPUTS
*      Flags - Specify one or more flag by logically OR'ing them
*         together.
*
*         CR_NONE - No flags, read from serial and console and don't
*             check for function key presses.
*         CR_NOSERIAL - Don't read from the serial device
*         CR_NOCONSOLE - Don't read from the console device.
*         CR_CHECKFUNC - Check and handle function key presses
*             e.g. so the sysop can kick users and chat, etc..
*
*         If the sysop presses the key to log a user off and you
*         don't specify CD_CHECKFUNC then you'll have to handle
*         that yourself (which you *really* don't want to have to
*         do!)
*
*   RESULT
*      TRUE if there is input to be checked.
*
*      if this function returns TRUE then check the first byte of
*      NodeData->CurrentLine for the actual keys that were pressed.
*
*      For ease of use, NodeData->CurrentLine[1] is set to NULL
*      which means you can still use NodeData->CurrentLine as a
*      null-terminated string.
*
*      If the input came from the serial device then
*      NodeData->CurrentLine[2] will be set to GET_SERIAL
*
*      If the input came from the watch window then
*      NodeData->CurrentLine[2] will be set to GET_CONSOLE
*
*      E.g. to implement Ctrl-C checking, you might do this:
*      if (DOOR_CheckRaw(CR_NONE) && N_ND->CurrentLine[0]==3) ...
*
*   NOTES
*      Do not call this function in loop with CR_NOSERIAL, because if
*      the watch window is not open then you'll sit there forever.
*      This function is processor intensive if called in a loop
*      as it keeps sending messages via a named port to the node
*      program (unavoidable).
*
*   SEE ALSO
*      DOOR_GetLine()
*
*********************************************************************/


V_BOOL __asm __saveds LIBDOOR_CheckRaw(register __d0 ULONG Flags)
{
  return(SendDoorIOMessage(DOORIO_CHECKRAW,NULL,0,Flags,NULL,0,0));
}

/****** HBBSNode.library/HBBS_TimeLeft *******************************
*
*   NAME
*      HBBS_TimeLeft - Get the amount of time the user has left.
*
*   SYNOPSIS
*      HBBS_TimeLeft( void )
*
*      LONG HBBS_TimeLeft( void )
*
*   FUNCTION
*      Get's the amount of time the current user has has got left.
*
*   INPUTS
*      None
*
*   RESULT
*      LONG - Time in minutes, if less than 0 then they've been
*          on-line too long, if 0 they may have unlimited time, so
*          do a HBBS_CheckAccess(ACS_UNLIMTIME) to see..
*
*   NOTES
*      Be careful when testing this, when it is used on a sysop
*      account the result will almost always be 0.
*
*   SEE ALSO
*      DOOR_TimeOnline(), HBBS_CheckAccess()
*
*********************************************************************/

LONG __asm __saveds LIBHBBS_TimeLeft( void )
{
  // return 0 if time limit is disabled, or the amount of time left..
  // therefore: only log people off if this returns <0, but ideally check for ACS_UNLIMTIME too...
  return((N_ND->User->Acs.Data[ACS_UNLIMTIME]=='Y') ? 0 : ((LONG)((N_ND->User->CallData->TimeAllowed+N_ND->User->CallData->ExtraTimeLimit)-N_ND->User->CallData->TimeUsed-LIBHBBS_TimeOnline())));

}

/****** HBBSNode.library/HBBS_TimeOnline *****************************
*
*   NAME
*      HBBS_TimeOnline - Get the amount of time the user's been online
*
*   SYNOPSIS
*      HBBS_TimeOnline( void )
*
*      LONG HBBS_TimeOnline( void )
*
*   FUNCTION
*      Get's the amount of time the current user has been online.
*
*   INPUTS
*      None
*
*   RESULT
*      LONG - Time in minutes
*
*   SEE ALSO
*      DOOR_TimeLeft()
*
*********************************************************************/

LONG __asm __saveds LIBHBBS_TimeOnline( void )
{
  ULONG tdiff;
    // current time, - time logged on (= secs online) / 60 = mins on-line
  tdiff = HBBS_GetAmigaTime();

  return((LONG)((tdiff-N_ND->User->CallData->LastCalledDate) / 60));
}

/****** HBBSNode.library/DOOR_Goodbye ********************************
*
*   NAME
*      DOOR_Goodbye - Perform logoff requirements
*
*   SYNOPSIS
*      DOOR_Goodbye( void )
*
*      void DOOR_Goodbye( void )
*
*   FUNCTION
*      This function performs all the required actions for a
*      sucessful logoff, it does NOT hang up the modem
*
*      Is is normally called by the LogOff door ("G") or the
*      re-logon door ("RL")
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      DOOR_HangUp()
*
*********************************************************************/

void __asm __saveds LIBDOOR_Goodbye( void )
{
  // Perform all actions required for a sucessful logoff..
  // (see Logout door for an example..)

  N_ND->OnlineStatus=OS_OFFLINE;

  // lostcarrier flag is set by default when a user has a successful login,
  // so we must UNset it!
  N_ND->Actions[ACTN_CARRIERLOST]=ACTC_NONE;
}

/****** HBBSNode.library/HBBS_AllowConfAccess ************************
*
*   NAME
*      HBBS_AllowConfAccess - Checks to see if a user has access to
*          a conference.
*
*   SYNOPSIS
*      HBBS_AllowConfAccess( confnum, User )
*                            D0       A0
*
*      BOOL HBBS_AllowConfAccess( V_BIGNUM confnum,
*          struct UserData *User )
*
*   FUNCTION
*      This function checks to see if a user has access to the
*      conference specified.
*
*   INPUTS
*      Confnum - number of a conference, from 1 to
*          BBSGlobal->Conferences
*
*      User - Pointer to a valid struct UserData, or NULL to check the
*          current node's current user.
*
*   RESULT
*      struct ConfData * - a pointer to a conference data structure,
*        or NULL if the user's last conference does not exist.
*
*   NOTES
*      This function should really be called HBBS_CheckConfAccess()
*
*   SEE ALSO
*      ValidConfNum(), FindConf()
*
*********************************************************************/

BOOL __asm __saveds LIBHBBS_AllowConfAccess(register __d0 V_BIGNUM confnum,register __a0 struct UserData *User)
{
  // confnum starts at 1.

  struct ConfData *tmpconf;
  struct BoolNode *boolnode;

  char tmpfilename[BIG_STR];
  struct UserData *CheckUser;
  struct ConfAcsData *CheckConfAcs;
  struct ConfAcsData ConfAcsDat;
  V_BOOL error=FALSE;
  BOOL retval=FALSE;
  int loop;
  struct Node *node;

  ConfAcsDat.Name=NULL; // initalise..
  ConfAcsDat.See=NULL; // initalise..
  ConfAcsDat.Access=NULL; // initalise..

  if (User==NULL)
  {
    CheckUser=N_ND->User->CallData;
    CheckConfAcs=&N_ND->User->ConfAcs;
  }
  else
  {
    CheckUser=User;

    if (ConfAcsDat.See=HBBS_CreateList())
    {
      for (loop=0;(!error) && (loop<BBSGlobal->Conferences);loop++)
      {
        if (node=AllocVec(sizeof(struct BoolNode),MEMF_CLEAR|MEMF_PUBLIC))
        {
          AddTail(ConfAcsDat.See,node);
        }
        else error=TRUE;
      }
    } else error=TRUE;

    if (ConfAcsDat.Access=HBBS_CreateList())
    {
      for (loop=0;(!error) && (loop<BBSGlobal->Conferences);loop++)
      {
        if (node=AllocVec(sizeof(struct BoolNode),MEMF_CLEAR|MEMF_PUBLIC))
        {
          AddTail(ConfAcsDat.Access,node);
        }
        else error=TRUE;
      }
    } else error=TRUE;

    if (error)
    {
      confnum=0; // forces a skip of the next section
    }
    else
    {
      sprintf(tmpfilename,"HBBS:System/Data/ConfAcs/%s.CFG",CheckUser->ConfAcsDataFile);
      if (LIBHBBS_LoadConfAcs(&ConfAcsDat,tmpfilename))
      {
        CheckConfAcs=&ConfAcsDat;
      }
      else
      {
        confnum=0; //to force a FALSE return...
      }
    }
  }

  if (LIBValidConfNum(confnum))
  {
    tmpconf=(struct ConfData *)GetNode(BBSGlobal->ConfList,confnum-1);

    // is access level required to enter conference greater than users access level ?
    if (!(tmpconf->ConfAccess > CheckUser->Access))
    {
      boolnode=(struct BoolNode *)GetNode(CheckConfAcs->Access,confnum-1);

      if (boolnode->Boolean)
      {
          /* check to see if the user is in the list of handles that are NOT allowed
           * to join this conference
           */

        if (tmpconf->UserNotAllowed)
        {
          retval = TRUE;

          if (HBBS_FindNode(tmpconf->UserNotAllowed,CheckUser->Handle))
          {
            retval = FALSE;
          }
        }
        else
        {
            /* check to see if the user is in the list of handles that ARE allowed
             * to join this conference
             */
          if (tmpconf->UserAllowed)
          {
            retval = FALSE;
            if (HBBS_FindNode(tmpconf->UserAllowed,CheckUser->Handle))
            {
              retval = TRUE;
            }
          }
          else
          {
            retval = TRUE; /* Neither UserNotAllowed or UserAllowed were specified */
          }
        }
      }
    }
  }

  if (User)
  {
    FreeStrList(ConfAcsDat.See);    // frees the list and it's nodes, this is ok even on a list of boolnodes
    FreeStrList(ConfAcsDat.Access); // because no strings are allocated and everything uses alloc/freevec()
  }

  return(retval);
}

/****** HBBSNode.library/HBBS_SetBBSStrings **************************
*
*   NAME
*      HBBS_SetBBSStrings - Force a re-load of the bbs strings.
*
*   SYNOPSIS
*      HBBS_SetBBSStrings( void )
*
*      void HBBS_SetBBSStrings( void )
*
*   FUNCTION
*      This function will force the node to re-load the BBSStrings.CFG
*      file from the current conference or node.
*
*      This is normally only used when changing conference and it
*      should really only be called by the JoinConference door.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_SetBBSCols(), HBBS_SetAccess()
*
*********************************************************************/

void __asm __saveds LIBHBBS_SetBBSStrings( void )
{
  struct CfgFileData *CfgFile;
  char filename[1024],*bbsstringsfile="BBSStrings.CFG";

  BOOL foundfile=FALSE;
  BYTE triedall=3;

  FreeAndSet(&N_ND->BBSStrings->PausePrompt,"Press [Return] to continue!");
  FreeAndSet(&N_ND->BBSStrings->OutOfTime,"Sorry you've run out of time");
  FreeAndSet(&N_ND->BBSStrings->InactivityTimeout,"\r\n\r\nInactivity Timeout!\r\n\r\n");
  FreeAndSet(&N_ND->BBSStrings->NoLoginsAllowed,"No logins are allowed at the moment, sysop has restricted access\r\n");

  do
  {
    triedall--;
    switch (triedall)
    {
      case 2:
        if (N_ND->CurrentConf)
        {
          strcpy(filename,N_ND->CurrentConf->ConfPath);
          strcat(filename,bbsstringsfile);
        } else filename[0]=0;
        break;
      case 1:
        strcpy(filename,N_ND->NodeLocation);
        strcat(filename,bbsstringsfile);
        break;
      case 0:
        strcpy(filename,"HBBS:System/Data/");
        strcat(filename,bbsstringsfile);
        break;
    }
    if (filename[0])
    {
      if (CfgFile=HBBS_LoadConfig(filename,LCFG_NOSTRIPCOMMENTS))
      {
        foundfile=TRUE;
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSStrings->PausePrompt,VTYPE_STRING,"PausePrompt",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSStrings->OutOfTime,VTYPE_STRING,"OutOfTime",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSStrings->InactivityTimeout,VTYPE_STRING,"InactivityTimeout",OPT_SINGLE);
        HBBS_GetSetting(CfgFile,(void *)&N_ND->BBSStrings->NoLoginsAllowed,VTYPE_STRING,"NoLoginsAllowed",OPT_SINGLE);

        HBBS_FlushConfig(CfgFile);
      }
    }
  } while ((!foundfile) && (triedall>0));
}

// DO NOT CALL FROM A DOOR.  as your door will get the signals, not the NODE program!

void __asm __saveds LIBHBBS_CleanupNodeConsoleWin( void )
{
  // do everything in reverse order..

  if (N_ND->ConScr)
  {
    if (N_ND->ConWin)
    {
      if (!N_ND->NodeSettings.UseOwnScreen) // only save settings for window rather than a screen...
      {
        N_ND->ConX=N_ND->ConWin->LeftEdge;
        N_ND->ConY=N_ND->ConWin->TopEdge;
        N_ND->ConW=N_ND->ConWin->Width;
        N_ND->ConH=N_ND->ConWin->Height;
      }
      if (N_ND->ConBuffer)
      {
        if (N_ND->ConRPort)
        {
          if (N_ND->ConWPort)
          {
            if (N_ND->ConWrite)
            {
              if (N_ND->ConRead)
              {
                // remove any pending requests..
                if (N_ND->ConWaiting) LIBAbortConRead();
                CloseDevice((struct IORequest*)N_ND->ConWrite);
                DeleteExtIO((struct IORequest*)N_ND->ConRead);
              }
              DeleteExtIO((struct IORequest*)N_ND->ConWrite);
            }
            DeletePort(N_ND->ConWPort);
          }
          DeletePort(N_ND->ConRPort);
        }
        FreeVec(N_ND->ConBuffer);
      }
      CloseWindow(N_ND->ConWin);
    }
    if (N_ND->NodeSettings.UseOwnScreen)
    {

      while (!CloseScreen(N_ND->ConScr)); // *C* possible hang... changeme!
    }
    else
    {
      UnlockPubScreen(NULL,N_ND->ConScr);
      N_ND->ConScr=NULL;
    }
  }
  N_ND->ConWin=NULL;
  N_ND->ConScr=NULL;
  N_ND->ConOK=FALSE;
  ScreenToFront(BBSGlobal->ScreenInfo->Scr); // put Main screen to front..
}

void __asm __saveds LIBHBBS_SetWatchTitles( void )
{
  // change the title of the window evenif the y are not open
  // so that when you do open then they are correct.


  sprintf(N_ND->ConWinTitle,"Node %d ",N_ND->NodeNum);

  if (N_ND->User->Valid)
  {
    sprintf(&N_ND->ConWinTitle[strlen(N_ND->ConWinTitle)],"%s %s (%s)",N_ND->User->CallData->Handle,N_ND->User->CallData->Group,N_ND->ConnectBaud);
  }

  // update them if they are open

  if (N_ND->ConOK) SetWindowTitles(N_ND->ConWin,N_ND->ConWinTitle,N_ND->ConWinTitle); // set screen title
}

// DO NOT CALL FROM A DOOR.  as your door will get the signals, and not the NODE program!

V_BOOL __asm __saveds LIBHBBS_OpenNodeConsoleWin( void )
{
  N_ND->ConOK=FALSE;
  sprintf(N_ND->ConScreenName,"NodeWatch_%d",N_ND->NodeNum);
  if (N_ND->NodeSettings.UseOwnScreen)
  {
    if (N_ND->ConScr=OpenScreenTags(NULL,
                                SA_Colors,  N_ND->ScrCols,
                                SA_Pens,  N_ND->DriPens,
                                SA_Depth,N_ND->NodeSettings.ScrDepth,
                                SA_Left,0,
                                SA_Top,0,
                                SA_Width,N_ND->NodeSettings.ScrWidth,
                                SA_Height,N_ND->NodeSettings.ScrHeight,
                                SA_DisplayID,N_ND->NodeSettings.ScrModeID,
                                SA_Overscan,1,
                                SA_AutoScroll,1,
                                SA_Font, N_ND->HBBSTextAttr,
//                                SA_SysFont, 0,
                                SA_PubName,N_ND->ConScreenName,
                                TAG_DONE))
    {
      PubScreenStatus(N_ND->ConScr,0);
    }

  }
  else
  {
    N_ND->ConScr=LockPubScreen(BBSGlobal->ScreenInfo->PubScreenName);
    strcpy(N_ND->ConScreenName,BBSGlobal->ScreenInfo->PubScreenName);
  }

  if (N_ND->ConScr)
  {
    if (N_ND->ConWin=OpenWindowTags(NULL,
                                    WA_CustomScreen,N_ND->ConScr,
                                    WA_Left,N_ND->NodeSettings.UseOwnScreen ? 0 : N_ND->ConX,
                                    WA_Top,N_ND->NodeSettings.UseOwnScreen ? 11 : N_ND->ConY,
                                    WA_Width,N_ND->NodeSettings.UseOwnScreen ? N_ND->NodeSettings.ScrWidth : N_ND->ConW,
                                    WA_Height,N_ND->NodeSettings.UseOwnScreen ? N_ND->NodeSettings.ScrHeight-11 : N_ND->ConH,
                                    WA_AutoAdjust,TRUE,
                                    WA_MinWidth,200,
                                    WA_MinHeight,50,
                                    WA_MaxHeight,65534,
                                    WA_MaxWidth,65534,
                                    WA_BlockPen,7,
                                    WA_DetailPen,0,
                                    WA_Flags,N_ND->NodeSettings.UseOwnScreen ? WFLG_BORDERLESS|WFLG_BACKDROP|WFLG_ACTIVATE : WFLG_DRAGBAR|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_SIZEGADGET|WFLG_SIZEBBOTTOM|WFLG_ACTIVATE,
                                    WA_IDCMP,IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|IDCMP_CHANGEWINDOW,
                                    WA_SmartRefresh,TRUE,
                                    TAG_DONE))
    {
      SetFont(N_ND->ConWin->RPort,N_ND->HBBSFont);
      if (N_ND->ConBuffer=AllocVec(DEF_CONBUFLEN,MEMF_PUBLIC))
      {
        N_ND->ConBufferLen=DEF_CONBUFLEN;
        if (N_ND->ConRPort=CreateMsgPort())
        {
          if (N_ND->ConWPort=CreateMsgPort())
          {
            if (N_ND->ConWrite = (struct IOStdReq *) CreateExtIO(N_ND->ConWPort,(LONG)sizeof(struct IOStdReq)))
            {
              if (N_ND->ConRead = (struct IOStdReq *) CreateExtIO(N_ND->ConRPort,(LONG)sizeof(struct IOStdReq)))
              {
                N_ND->ConWrite->io_Data = (APTR) N_ND->ConWin;
                N_ND->ConWrite->io_Length = sizeof(struct Window);

                if (!(OpenDevice("console.device", CONU_SNIPMAP, (struct IORequest*)N_ND->ConWrite, 0)))
                {
                  N_ND->ConRead->io_Device = N_ND->ConWrite->io_Device;
                  N_ND->ConRead->io_Unit   = N_ND->ConWrite->io_Unit;
                  N_ND->ConWaiting=FALSE;
                  N_ND->ConOK=TRUE;
                  LIBHBBS_SetWatchTitles();
                  ScreenToFront(N_ND->ConScr); // put Console Screen to front..
                  return(TRUE);
                }
              }
            }
          }
        }
      }
    }
  }
  LIBHBBS_CleanupNodeConsoleWin();

  return(FALSE);
}

// DO NOT CALL FROM A DOOR.  as your door will get the signals, not the NODE program!

void __asm __saveds LIBHBBS_ChangeConsoleMode( register __d0 short Mode )
{
  if (Mode==2)
  {
    if (N_ND->NodeSettings.UseOwnScreen) Mode=1; else Mode=0;
  }
  if (Mode==0) // use screen
  {
    if (!N_ND->NodeSettings.UseOwnScreen)
    {
      if (N_ND->ConOK) LIBHBBS_CleanupNodeConsoleWin();
      // don't change the setting until the console window
      // and or screen has been closed otherwise it will try to close
      // the screen rather than unlocking it :-)
      N_ND->NodeSettings.UseOwnScreen=TRUE;
      LIBHBBS_OpenNodeConsoleWin();
    }
  }
  if (Mode==1) // use window
  {
    if (N_ND->NodeSettings.UseOwnScreen)
    {
      if (N_ND->ConOK) LIBHBBS_CleanupNodeConsoleWin();
      N_ND->NodeSettings.UseOwnScreen=FALSE;
      LIBHBBS_OpenNodeConsoleWin();
    }
  }
}


void __asm __saveds LIBDOOR_ChangeConsoleMode( register __d0 short Mode )
{
  SendDoorIOMessage(DOORIO_CHANGECONSOLEMODE,NULL,0,NULL,NULL,Mode,0);
}

V_BOOL __asm __saveds LIBDOOR_CheckCSI( register __d0 char keychar,register __d1 short wherefrom )
{
  char mystr[2];
  mystr[0] = keychar;

  return(SendDoorIOMessage(DOORIO_CHECKCSI,mystr,1,NULL,NULL,wherefrom,0));
}

void __asm __saveds LIBDOOR_Add_Last_Pager( register __a0 UBYTE *details )
{
  QuickDoorIOMessage(DOORIO_ADDLASTPAGER,details);
}

void __asm __saveds LIBDOOR_Add_Last_PWFail( register __a0 UBYTE *details )
{
  QuickDoorIOMessage(DOORIO_ADDLASTPWFAIL,details);
}

void __asm __saveds LIBDOOR_Add_Last_Carrier( register __a0 UBYTE *details )
{
  QuickDoorIOMessage(DOORIO_ADDLASTCARRIER,details);
}

/****** HBBSNode.library/DOOR_DisplayBulletin ************************
*
*   NAME
*      DOOR_DisplayBulletin - Display a special screen.
*
*   SYNOPSIS
*      DOOR_DisplayBulletin( BulletinName )
*                                 A0
*
*      V_BOOL DOOR_DisplayBulletin( UBYTE *BulletinName )
*
*   FUNCTION
*      This function searches for a bulletin in the same manor
*      that DOOR_DisplaySpecialScreen() uses, as described
*      in HBBS:Docs/Reference/Screens.Guide, but instead of looking
*      in the Screens/Special directory, it looks in the
*      Screens/Bulletins directory instead, it also adds the user's
*      selected language extension to the filename as defined by
*      BBSGlobal/LanguageName_XX and BBSGlobal/LanguageExtn_XX.
*
*      If a screen file is found, the filename is then passed to
*      DOOR_DisplayScreen(), see that for further information.
*
*      Only the first screen file that is found will be displayed.
*
*      After displaying a screen it is good practise to check if the
*      node is still on-line.  The user may have lost carrier while
*      the screen was being displayed, or another door may have been
*      called by a @^@ code which hung up the modem, etc.
*
*      So check NodeStatus->OnlineStatus after each call.
*
*   INPUTS
*      BulletinName - null terminated bulletin name.
*
*   RESULT
*      TRUE if a screen was displayed.
*
*   SEE ALSO
*      DOOR_DisplayScreen(), HBBS:Docs/Reference/Screens.Guide
*      BBSGlobal/LanguageName_XX, BBSGlobal/LanguageExtn_XX
*      DOOR_DisplaySpecialScreen()
*
*********************************************************************/

V_BOOL __asm __saveds LIBDOOR_DisplayBulletin( register __a0 UBYTE *BulletinName )
{
  return(QuickDoorIOMessage(DOORIO_DISPLAYBULLETIN,BulletinName));
}

